feat(webpack): add webpack plugin (#11966)
This commit is contained in:
parent
a914f78701
commit
f49769a34a
@ -41,6 +41,7 @@ module.exports = {
|
|||||||
description: 'anything testing specific (e.g., jest or cypress)',
|
description: 'anything testing specific (e.g., jest or cypress)',
|
||||||
},
|
},
|
||||||
{ name: 'web', description: 'anything Web specific' },
|
{ name: 'web', description: 'anything Web specific' },
|
||||||
|
{ name: 'webpack', description: 'anything Webpack specific' },
|
||||||
],
|
],
|
||||||
|
|
||||||
allowTicketNumber: true,
|
allowTicketNumber: true,
|
||||||
|
|||||||
@ -26,6 +26,12 @@
|
|||||||
"description": "Init Web Plugin.",
|
"description": "Init Web Plugin.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"bundler": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"enum": ["webpack", "none"],
|
||||||
|
"default": "webpack"
|
||||||
|
},
|
||||||
"unitTestRunner": {
|
"unitTestRunner": {
|
||||||
"description": "Adds the specified unit test runner",
|
"description": "Adds the specified unit test runner",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -105,6 +111,12 @@
|
|||||||
"enum": ["babel", "swc"],
|
"enum": ["babel", "swc"],
|
||||||
"default": "babel"
|
"default": "babel"
|
||||||
},
|
},
|
||||||
|
"bundler": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"enum": ["webpack", "none"],
|
||||||
|
"default": "webpack"
|
||||||
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
780
docs/generated/packages/webpack.json
Normal file
780
docs/generated/packages/webpack.json
Normal file
@ -0,0 +1,780 @@
|
|||||||
|
{
|
||||||
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
|
"name": "webpack",
|
||||||
|
"packageName": "@nrwl/webpack",
|
||||||
|
"description": "The Nx Plugin for Webpack contains executors and generators that support building applications using Webpack",
|
||||||
|
"root": "/packages/webpack",
|
||||||
|
"source": "/packages/webpack/src",
|
||||||
|
"documentation": [],
|
||||||
|
"generators": [
|
||||||
|
{
|
||||||
|
"name": "init",
|
||||||
|
"factory": "./src/generators/init/init#webpackInitGenerator",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxWebpackInit",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Init Webpack Plugin",
|
||||||
|
"description": "Init Webpack Plugin.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"compiler": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["babel", "swc", "tsc"],
|
||||||
|
"description": "The compiler to initialize for.",
|
||||||
|
"default": "babel"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"description": "Skip formatting files.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Initialize the `@nrwl/webpack` plugin.",
|
||||||
|
"aliases": ["ng-add"],
|
||||||
|
"hidden": true,
|
||||||
|
"implementation": "/packages/webpack/src/generators/init/init#webpackInitGenerator.ts",
|
||||||
|
"path": "/packages/webpack/src/generators/init/schema.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "webpack-project",
|
||||||
|
"factory": "./src/generators/webpack-project/webpack-project#webpackProjectGenerator",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxWebpackProject",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Add Webpack Configuration to a project",
|
||||||
|
"description": "Add Webpack Configuration to a project.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the project.",
|
||||||
|
"$default": { "$source": "argv", "index": 0 },
|
||||||
|
"x-dropdown": "project",
|
||||||
|
"x-prompt": "What is the name of the project to set up a webpack for?"
|
||||||
|
},
|
||||||
|
"compiler": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["babel", "swc", "tsc"],
|
||||||
|
"description": "The compiler to use to build source.",
|
||||||
|
"default": "babel"
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/main.ts'."
|
||||||
|
},
|
||||||
|
"tsConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path relative to the workspace root for the tsconfig file to build with. Defaults to '<projectRoot>/tsconfig.app.json'."
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target platform for the build, same as the Webpack config option.",
|
||||||
|
"enum": ["node", "web"],
|
||||||
|
"default": "web"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"description": "Skip formatting files.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"skipPackageJson": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Do not add dependencies to `package.json`."
|
||||||
|
},
|
||||||
|
"devServer": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Add a serve target to run a local webpack dev-server",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"webpackConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path relative to workspace root to a custom webpack file that takes a config object and returns an updated config."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [],
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Add webpack configuration to a project.",
|
||||||
|
"hidden": true,
|
||||||
|
"implementation": "/packages/webpack/src/generators/webpack-project/webpack-project#webpackProjectGenerator.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"path": "/packages/webpack/src/generators/webpack-project/schema.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"executors": [
|
||||||
|
{
|
||||||
|
"name": "webpack",
|
||||||
|
"implementation": "/packages/webpack/src/executors/webpack/webpack.impl.ts",
|
||||||
|
"schema": {
|
||||||
|
"title": "Webpack Executor",
|
||||||
|
"description": "Builds web applications using webpack.",
|
||||||
|
"cli": "nx",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"crossOrigin": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The `crossorigin` attribute to use for generated javascript script tags. One of 'none' | 'anonymous' | 'use-credentials'."
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the main entry-point file.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.js|.ts|.tsx)"
|
||||||
|
},
|
||||||
|
"tsConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the Typescript configuration file.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "tsconfig.*.json"
|
||||||
|
},
|
||||||
|
"compiler": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The compiler to use.",
|
||||||
|
"enum": ["babel", "swc", "tsc"],
|
||||||
|
"default": "babel"
|
||||||
|
},
|
||||||
|
"outputPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The output path of the generated files.",
|
||||||
|
"x-completion-type": "directory"
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target platform for the build, same as the Webpack config option.",
|
||||||
|
"enum": ["node", "web"],
|
||||||
|
"default": "web"
|
||||||
|
},
|
||||||
|
"deleteOutputPath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Delete the output path before building.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"watch": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable re-building when files change.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"baseHref": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Base url for the application being built."
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"runtimeChunk": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Use a separate bundle containing the runtime.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"sourceMap": {
|
||||||
|
"description": "Output sourcemaps. Use 'hidden' for use with error reporting tools without generating sourcemap comment.",
|
||||||
|
"default": true,
|
||||||
|
"oneOf": [{ "type": "boolean" }, { "type": "string" }]
|
||||||
|
},
|
||||||
|
"progress": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Log progress to the console while building.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"assets": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of static application assets.",
|
||||||
|
"default": [],
|
||||||
|
"items": {
|
||||||
|
"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" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "HTML File which will be contain the application.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.html|.htm)"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "External Scripts which will be included before the main application entry.",
|
||||||
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"input": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to include.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.css|.scss|.less|.sass|.styl|.stylus)"
|
||||||
|
},
|
||||||
|
"bundleName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundle name for this extra entry point."
|
||||||
|
},
|
||||||
|
"inject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If the bundle will be referenced in the HTML file.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["input"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to include.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.css|.scss|.less|.sass|.styl|.stylus)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "External Styles which will be included with the application",
|
||||||
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"input": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to include.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.css|.scss|.less|.sass|.styl|.stylus)"
|
||||||
|
},
|
||||||
|
"bundleName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundle name for this extra entry point."
|
||||||
|
},
|
||||||
|
"inject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If the bundle will be referenced in the HTML file.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["input"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to include.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.css|.scss|.less|.sass|.styl|.stylus)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"budgets": {
|
||||||
|
"description": "Budget thresholds to ensure parts of your application stay within boundaries which you set.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"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"]
|
||||||
|
},
|
||||||
|
"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" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"generatePackageJson": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Generates a `package.json` file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"transformers": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of TypeScript Compiler Transfomers Plugins.",
|
||||||
|
"default": [],
|
||||||
|
"aliases": ["tsPlugins"],
|
||||||
|
"items": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string" },
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"options": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalEntryPoints": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"entryName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the additional entry file."
|
||||||
|
},
|
||||||
|
"entryPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the additional entry file.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.js|.ts)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputFileName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the main output file.",
|
||||||
|
"default": "main.js"
|
||||||
|
},
|
||||||
|
"externalDependencies": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string", "enum": ["none", "all"] },
|
||||||
|
{ "type": "array", "items": { "type": "string" } }
|
||||||
|
],
|
||||||
|
"description": "Dependencies to keep external to the bundle. (`all` (default), `none`, or an array of module names)",
|
||||||
|
"default": "all"
|
||||||
|
},
|
||||||
|
"extractCss": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Extract CSS into a `.css` file.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"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",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.js|.ts|.tsx)"
|
||||||
|
},
|
||||||
|
"verbose": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Emits verbose output",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"memoryLimit": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Memory limit for type checking service process in `MB`.",
|
||||||
|
"default": 2048
|
||||||
|
},
|
||||||
|
"maxWorkers": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Number of workers to use for type checking.",
|
||||||
|
"default": 2
|
||||||
|
},
|
||||||
|
"fileReplacements": {
|
||||||
|
"description": "Replace files with other files in the build.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"replace": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to be replaced.",
|
||||||
|
"x-completion-type": "file"
|
||||||
|
},
|
||||||
|
"with": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to replace with.",
|
||||||
|
"x-completion-type": "file"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["replace", "with"]
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"buildLibsFromSource": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Read buildable libraries from source instead of building them separately.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"generateIndexHtml": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Generates `index.html` file to the output path. This can be turned off if using a webpack plugin to generate HTML such as `html-webpack-plugin`.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"postcssConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Set a path to PostCSS config that applies to the app and all libs. Defaults to `undefined`, which auto-detects postcss.config.js files in each `app`/`lib` directory."
|
||||||
|
},
|
||||||
|
"webpackConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to a function which takes a webpack config, some context and returns the resulting webpack config. See https://nx.dev/guides/customize-webpack",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "webpack?(*)@(.js|.ts)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["tsConfig", "main"],
|
||||||
|
"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"]
|
||||||
|
},
|
||||||
|
"extraEntryPoint": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"input": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to include.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.css|.scss|.less|.sass|.styl|.stylus)"
|
||||||
|
},
|
||||||
|
"bundleName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundle name for this extra entry point."
|
||||||
|
},
|
||||||
|
"inject": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If the bundle will be referenced in the HTML file.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["input"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file to include.",
|
||||||
|
"x-completion-type": "file",
|
||||||
|
"x-completion-glob": "**/*@(.css|.scss|.less|.sass|.styl|.stylus)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"transformerPattern": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string" },
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"options": { "type": "object", "additionalProperties": true }
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Run webpack build.",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/webpack/src/executors/webpack/schema.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dev-server",
|
||||||
|
"implementation": "/packages/webpack/src/executors/dev-server/dev-server.impl.ts",
|
||||||
|
"schema": {
|
||||||
|
"title": "Web Dev Server",
|
||||||
|
"description": "Serve a web application.",
|
||||||
|
"cli": "nx",
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"hmr": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable hot module replacement.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"publicHost": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public URL where the application will be served."
|
||||||
|
},
|
||||||
|
"open": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Open the application in the browser.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"allowedHosts": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "This option allows you to whitelist services that are allowed to access the dev server."
|
||||||
|
},
|
||||||
|
"memoryLimit": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Memory limit for type checking service process in `MB`."
|
||||||
|
},
|
||||||
|
"maxWorkers": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Number of workers to use for type checking."
|
||||||
|
},
|
||||||
|
"baseHref": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Base url for the application being built."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Serve a web application.",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/webpack/src/executors/dev-server/schema.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -303,6 +303,15 @@
|
|||||||
"generators": ["init", "application"]
|
"generators": ["init", "application"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "webpack",
|
||||||
|
"packageName": "webpack",
|
||||||
|
"path": "generated/packages/webpack.json",
|
||||||
|
"schemas": {
|
||||||
|
"executors": ["webpack", "dev-server"],
|
||||||
|
"generators": ["init", "webpack-project"]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "workspace",
|
"name": "workspace",
|
||||||
"packageName": "workspace",
|
"packageName": "workspace",
|
||||||
|
|||||||
@ -29,9 +29,6 @@ describe('js e2e', () => {
|
|||||||
expect(libPackageJson.scripts.test).toBeDefined();
|
expect(libPackageJson.scripts.test).toBeDefined();
|
||||||
expect(libPackageJson.scripts.build).toBeDefined();
|
expect(libPackageJson.scripts.build).toBeDefined();
|
||||||
expect(runCLI(`test ${npmScriptsLib}`)).toContain('implement test');
|
expect(runCLI(`test ${npmScriptsLib}`)).toContain('implement test');
|
||||||
expect(runCLI(`test ${npmScriptsLib}`)).toContain(
|
|
||||||
'existing outputs match the cache, left as is'
|
|
||||||
);
|
|
||||||
|
|
||||||
const tsconfig = readJson(`tsconfig.base.json`);
|
const tsconfig = readJson(`tsconfig.base.json`);
|
||||||
expect(tsconfig.compilerOptions.paths).toEqual({
|
expect(tsconfig.compilerOptions.paths).toEqual({
|
||||||
@ -47,9 +44,6 @@ describe('js e2e', () => {
|
|||||||
expect((await runCLIAsync(`test ${lib}`)).combinedOutput).toContain(
|
expect((await runCLIAsync(`test ${lib}`)).combinedOutput).toContain(
|
||||||
'Ran all test suites'
|
'Ran all test suites'
|
||||||
);
|
);
|
||||||
expect((await runCLIAsync(`test ${lib}`)).combinedOutput).toContain(
|
|
||||||
'local cache'
|
|
||||||
);
|
|
||||||
|
|
||||||
const packageJson = readJson('package.json');
|
const packageJson = readJson('package.json');
|
||||||
const devPackageNames = Object.keys(packageJson.devDependencies);
|
const devPackageNames = Object.keys(packageJson.devDependencies);
|
||||||
@ -115,9 +109,6 @@ describe('js e2e', () => {
|
|||||||
expect((await runCLIAsync(`test ${parentLib}`)).combinedOutput).toContain(
|
expect((await runCLIAsync(`test ${parentLib}`)).combinedOutput).toContain(
|
||||||
'Ran all test suites'
|
'Ran all test suites'
|
||||||
);
|
);
|
||||||
expect((await runCLIAsync(`test ${parentLib}`)).combinedOutput).toContain(
|
|
||||||
'local cache'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(runCLI(`build ${parentLib}`)).toContain(
|
expect(runCLI(`build ${parentLib}`)).toContain(
|
||||||
'Done compiling TypeScript files'
|
'Done compiling TypeScript files'
|
||||||
@ -181,9 +172,6 @@ describe('js e2e', () => {
|
|||||||
expect((await runCLIAsync(`test ${lib}`)).combinedOutput).toContain(
|
expect((await runCLIAsync(`test ${lib}`)).combinedOutput).toContain(
|
||||||
'Ran all test suites'
|
'Ran all test suites'
|
||||||
);
|
);
|
||||||
expect((await runCLIAsync(`test ${lib}`)).combinedOutput).toContain(
|
|
||||||
'local cache'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(runCLI(`build ${lib}`)).toContain(
|
expect(runCLI(`build ${lib}`)).toContain(
|
||||||
'Successfully compiled: 2 files with swc'
|
'Successfully compiled: 2 files with swc'
|
||||||
@ -205,9 +193,6 @@ describe('js e2e', () => {
|
|||||||
expect((await runCLIAsync(`test ${parentLib}`)).combinedOutput).toContain(
|
expect((await runCLIAsync(`test ${parentLib}`)).combinedOutput).toContain(
|
||||||
'Ran all test suites'
|
'Ran all test suites'
|
||||||
);
|
);
|
||||||
expect((await runCLIAsync(`test ${parentLib}`)).combinedOutput).toContain(
|
|
||||||
'local cache'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(runCLI(`build ${parentLib}`)).toContain(
|
expect(runCLI(`build ${parentLib}`)).toContain(
|
||||||
'Successfully compiled: 2 files with swc'
|
'Successfully compiled: 2 files with swc'
|
||||||
|
|||||||
@ -352,7 +352,7 @@ ${jslib}();
|
|||||||
const nestapp = uniq('nestapp');
|
const nestapp = uniq('nestapp');
|
||||||
runCLI(`generate @nrwl/nest:app ${nestapp} --linter=eslint`);
|
runCLI(`generate @nrwl/nest:app ${nestapp} --linter=eslint`);
|
||||||
|
|
||||||
packageInstall('@nestjs/swagger', undefined, '~5.0.0');
|
packageInstall('@nestjs/swagger', undefined, '^6.0.0');
|
||||||
|
|
||||||
updateProjectConfig(nestapp, (config) => {
|
updateProjectConfig(nestapp, (config) => {
|
||||||
config.targets.build.options.tsPlugins = ['@nestjs/swagger/plugin'];
|
config.targets.build.options.tsPlugins = ['@nestjs/swagger/plugin'];
|
||||||
@ -396,16 +396,8 @@ ${jslib}();
|
|||||||
await runCLIAsync(`build ${nestapp}`);
|
await runCLIAsync(`build ${nestapp}`);
|
||||||
|
|
||||||
const mainJs = readFile(`dist/apps/${nestapp}/main.js`);
|
const mainJs = readFile(`dist/apps/${nestapp}/main.js`);
|
||||||
expect(stripIndents`${mainJs}`).toContain(
|
expect(mainJs).toContain('FooDto');
|
||||||
stripIndents`
|
expect(mainJs).toContain('_OPENAPI_METADATA_FACTORY');
|
||||||
class FooDto {
|
|
||||||
static _OPENAPI_METADATA_FACTORY() {
|
|
||||||
return { foo: { required: true, type: () => String }, bar: { required: true, type: () => Number } };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.FooDto = FooDto;
|
|
||||||
`
|
|
||||||
);
|
|
||||||
}, 300000);
|
}, 300000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -80,7 +80,7 @@ describe('React Applications', () => {
|
|||||||
checkFilesExist(...filesToCheck);
|
checkFilesExist(...filesToCheck);
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/index.html`)).toContain(
|
expect(readFile(`dist/apps/${appName}/index.html`)).toContain(
|
||||||
`<script src="main.esm.js" type="module"></script><script src="main.es5.js" nomodule defer></script>`
|
`<script src="main.js" type="module"></script><script src="main.es5.js" nomodule defer></script>`
|
||||||
);
|
);
|
||||||
}, 250_000);
|
}, 250_000);
|
||||||
|
|
||||||
@ -125,13 +125,13 @@ describe('React Applications', () => {
|
|||||||
);
|
);
|
||||||
const filesToCheck = [
|
const filesToCheck = [
|
||||||
`dist/apps/${appName}/index.html`,
|
`dist/apps/${appName}/index.html`,
|
||||||
`dist/apps/${appName}/runtime.esm.js`,
|
`dist/apps/${appName}/runtime.js`,
|
||||||
`dist/apps/${appName}/polyfills.esm.js`,
|
`dist/apps/${appName}/polyfills.js`,
|
||||||
`dist/apps/${appName}/main.esm.js`,
|
`dist/apps/${appName}/main.js`,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (opts.checkSourceMap) {
|
if (opts.checkSourceMap) {
|
||||||
filesToCheck.push(`dist/apps/${appName}/main.esm.js.map`);
|
filesToCheck.push(`dist/apps/${appName}/main.js.map`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.checkStyles) {
|
if (opts.checkStyles) {
|
||||||
@ -214,9 +214,9 @@ describe('React Applications: additional packages', () => {
|
|||||||
|
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
`dist/apps/${appName}/index.html`,
|
`dist/apps/${appName}/index.html`,
|
||||||
`dist/apps/${appName}/runtime.esm.js`,
|
`dist/apps/${appName}/runtime.js`,
|
||||||
`dist/apps/${appName}/polyfills.esm.js`,
|
`dist/apps/${appName}/polyfills.js`,
|
||||||
`dist/apps/${appName}/main.esm.js`
|
`dist/apps/${appName}/main.js`
|
||||||
);
|
);
|
||||||
}, 250_000);
|
}, 250_000);
|
||||||
|
|
||||||
|
|||||||
@ -301,6 +301,7 @@ export function newProject({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(jack): we should tag the projects (e.g. tags: ['package']) and filter from that rather than hard-code packages.
|
||||||
const packages = [
|
const packages = [
|
||||||
`@nrwl/angular`,
|
`@nrwl/angular`,
|
||||||
`@nrwl/eslint-plugin-nx`,
|
`@nrwl/eslint-plugin-nx`,
|
||||||
@ -315,6 +316,7 @@ export function newProject({
|
|||||||
`@nrwl/react`,
|
`@nrwl/react`,
|
||||||
`@nrwl/storybook`,
|
`@nrwl/storybook`,
|
||||||
`@nrwl/web`,
|
`@nrwl/web`,
|
||||||
|
`@nrwl/webpack`,
|
||||||
`@nrwl/react-native`,
|
`@nrwl/react-native`,
|
||||||
];
|
];
|
||||||
packageInstall(packages.join(` `), projScope);
|
packageInstall(packages.join(` `), projScope);
|
||||||
|
|||||||
@ -31,9 +31,9 @@ describe('Web Components Applications', () => {
|
|||||||
runCLI(`build ${appName} --outputHashing none --compiler babel`);
|
runCLI(`build ${appName} --outputHashing none --compiler babel`);
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
`dist/apps/${appName}/index.html`,
|
`dist/apps/${appName}/index.html`,
|
||||||
`dist/apps/${appName}/runtime.esm.js`,
|
`dist/apps/${appName}/runtime.js`,
|
||||||
`dist/apps/${appName}/polyfills.esm.js`,
|
`dist/apps/${appName}/polyfills.js`,
|
||||||
`dist/apps/${appName}/main.esm.js`,
|
`dist/apps/${appName}/main.js`,
|
||||||
`dist/apps/${appName}/styles.css`
|
`dist/apps/${appName}/styles.css`
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ describe('Web Components Applications', () => {
|
|||||||
runCLI(`build ${appName} --outputHashing=none`);
|
runCLI(`build ${appName} --outputHashing=none`);
|
||||||
|
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
`dist/apps/${appName}/main.esm.js`,
|
`dist/apps/${appName}/main.js`,
|
||||||
`dist/apps/${appName}/main.es5.js`
|
`dist/apps/${appName}/main.es5.js`
|
||||||
);
|
);
|
||||||
}, 120000);
|
}, 120000);
|
||||||
@ -159,7 +159,7 @@ describe('Web Components Applications', () => {
|
|||||||
});
|
});
|
||||||
runCLI(`build ${appName} --outputHashing none`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/main.esm.js`)).toMatch(
|
expect(readFile(`dist/apps/${appName}/main.js`)).toMatch(
|
||||||
/Reflect\.metadata/
|
/Reflect\.metadata/
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ describe('Web Components Applications', () => {
|
|||||||
|
|
||||||
runCLI(`build ${appName} --outputHashing none`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
|
|
||||||
expect(readFile(`dist/apps/${appName}/main.esm.js`)).not.toMatch(
|
expect(readFile(`dist/apps/${appName}/main.js`)).not.toMatch(
|
||||||
/Reflect\.metadata/
|
/Reflect\.metadata/
|
||||||
);
|
);
|
||||||
}, 120000);
|
}, 120000);
|
||||||
@ -207,7 +207,7 @@ describe('Web Components Applications', () => {
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
runCLI(`build ${appName} --outputHashing none`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
checkFilesExist(`dist/apps/${appName}/main.esm.js`);
|
checkFilesExist(`dist/apps/${appName}/main.js`);
|
||||||
|
|
||||||
rmDist();
|
rmDist();
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ describe('Web Components Applications', () => {
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
runCLI(`build ${appName} --outputHashing none`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
checkFilesExist(`dist/apps/${appName}/main.esm.js`);
|
checkFilesExist(`dist/apps/${appName}/main.js`);
|
||||||
|
|
||||||
rmDist();
|
rmDist();
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ describe('Web Components Applications', () => {
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
runCLI(`build ${appName} --outputHashing none`);
|
runCLI(`build ${appName} --outputHashing none`);
|
||||||
checkFilesExist(`dist/apps/${appName}/main.esm.js`);
|
checkFilesExist(`dist/apps/${appName}/main.js`);
|
||||||
}, 100000);
|
}, 100000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
11
e2e/webpack/jest.config.ts
Normal file
11
e2e/webpack/jest.config.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||||
|
maxWorkers: 1,
|
||||||
|
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
|
||||||
|
displayName: 'e2e-webpack',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
};
|
||||||
34
e2e/webpack/project.json
Normal file
34
e2e/webpack/project.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "e2e/webpack",
|
||||||
|
"projectType": "application",
|
||||||
|
"targets": {
|
||||||
|
"e2e": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"options": {
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "yarn e2e-start-local-registry"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "yarn e2e-build-package-publish"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "nx run-e2e-tests e2e-webpack"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parallel": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"run-e2e-tests": {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "e2e/webpack/jest.config.ts",
|
||||||
|
"passWithNoTests": true,
|
||||||
|
"runInBand": true
|
||||||
|
},
|
||||||
|
"outputs": ["coverage/e2e/webpack"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"implicitDependencies": ["webpack"]
|
||||||
|
}
|
||||||
58
e2e/webpack/src/webpack.test.ts
Normal file
58
e2e/webpack/src/webpack.test.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import {
|
||||||
|
cleanupProject,
|
||||||
|
newProject,
|
||||||
|
rmDist,
|
||||||
|
runCLI,
|
||||||
|
runCommand,
|
||||||
|
uniq,
|
||||||
|
updateFile,
|
||||||
|
updateProjectConfig,
|
||||||
|
} from '@nrwl/e2e/utils';
|
||||||
|
|
||||||
|
describe('Webpack Plugin', () => {
|
||||||
|
beforeEach(() => newProject());
|
||||||
|
afterEach(() => cleanupProject());
|
||||||
|
|
||||||
|
it('should be able to setup project to build node programs with webpack and different compilers', async () => {
|
||||||
|
const myPkg = uniq('my-pkg');
|
||||||
|
runCLI(`generate @nrwl/js:lib ${myPkg} --buildable=false`);
|
||||||
|
updateFile(`libs/${myPkg}/src/index.ts`, `console.log('Hello');\n`);
|
||||||
|
|
||||||
|
// babel (default)
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/webpack:webpack-project ${myPkg} --target=node --tsConfig=libs/${myPkg}/tsconfig.lib.json --main=libs/${myPkg}/src/index.ts`
|
||||||
|
);
|
||||||
|
rmDist();
|
||||||
|
runCLI(`build ${myPkg}`);
|
||||||
|
let output = runCommand(`node dist/libs/${myPkg}/main.js`);
|
||||||
|
expect(output).toMatch(/Hello/);
|
||||||
|
|
||||||
|
updateProjectConfig(myPkg, (config) => {
|
||||||
|
delete config.targets.build;
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
// swc
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/webpack:webpack-project ${myPkg} --target=node --tsConfig=libs/${myPkg}/tsconfig.lib.json --main=libs/${myPkg}/src/index.ts --compiler=swc`
|
||||||
|
);
|
||||||
|
rmDist();
|
||||||
|
runCLI(`build ${myPkg}`);
|
||||||
|
output = runCommand(`node dist/libs/${myPkg}/main.js`);
|
||||||
|
expect(output).toMatch(/Hello/);
|
||||||
|
|
||||||
|
updateProjectConfig(myPkg, (config) => {
|
||||||
|
delete config.targets.build;
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
// tsc
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/webpack:webpack-project ${myPkg} --target=node --tsConfig=libs/${myPkg}/tsconfig.lib.json --main=libs/${myPkg}/src/index.ts --compiler=tsc`
|
||||||
|
);
|
||||||
|
rmDist();
|
||||||
|
runCLI(`build ${myPkg}`);
|
||||||
|
output = runCommand(`node dist/libs/${myPkg}/main.js`);
|
||||||
|
expect(output).toMatch(/Hello/);
|
||||||
|
}, 500000);
|
||||||
|
});
|
||||||
13
e2e/webpack/tsconfig.json
Normal file
13
e2e/webpack/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node", "jest"]
|
||||||
|
},
|
||||||
|
"include": [],
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
20
e2e/webpack/tsconfig.spec.json
Normal file
20
e2e/webpack/tsconfig.spec.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.spec.tsx",
|
||||||
|
"**/*.test.tsx",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.test.js",
|
||||||
|
"**/*.spec.jsx",
|
||||||
|
"**/*.test.jsx",
|
||||||
|
"**/*.d.ts",
|
||||||
|
"jest.config.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -6,7 +6,7 @@ import { assertTextOnPage } from './helpers';
|
|||||||
*/
|
*/
|
||||||
describe('nx-dev: Packages Section', () => {
|
describe('nx-dev: Packages Section', () => {
|
||||||
(<{ title: string; path: string }[]>[
|
(<{ title: string; path: string }[]>[
|
||||||
{ title: '@nrwl/', path: '/packages/angular' },
|
{ title: '@nrwl/angular', path: '/packages/angular' },
|
||||||
{
|
{
|
||||||
title: '@nrwl/angular:add-linting',
|
title: '@nrwl/angular:add-linting',
|
||||||
path: '/packages/angular/generators/add-linting',
|
path: '/packages/angular/generators/add-linting',
|
||||||
|
|||||||
4
nx-dev/nx-dev/public/images/icons/webpack.svg
Normal file
4
nx-dev/nx-dev/public/images/icons/webpack.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||||
|
<title>webpack</title>
|
||||||
|
<path d="M28.021 24.161l-11.552 6.505v-5.068l7.198-3.943zM28.813 23.448v-13.609l-4.229 2.432v8.745zM3.901 24.161l11.552 6.505v-5.068l-7.198-3.943zM3.109 23.448v-13.609l4.229 2.432v8.745zM3.604 8.958l11.849-6.672v4.901l-7.646 4.188zM28.318 8.958l-11.849-6.672v4.901l7.646 4.188zM15.453 24.448l-7.099-3.891v-7.703l7.099 4.083zM16.469 24.448l7.099-3.891v-7.703l-7.099 4.083zM8.833 11.964l7.13-3.901 7.13 3.901-7.13 4.099z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 556 B |
@ -18,5 +18,6 @@ export const iconsMap: Record<string, string> = {
|
|||||||
'react-native': '/images/icons/react.svg',
|
'react-native': '/images/icons/react.svg',
|
||||||
storybook: '/images/icons/storybook.svg',
|
storybook: '/images/icons/storybook.svg',
|
||||||
web: '/images/icons/html5.svg',
|
web: '/images/icons/html5.svg',
|
||||||
|
webpack: '/images/icons/webpack.svg',
|
||||||
workspace: '/images/icons/nx.svg',
|
workspace: '/images/icons/nx.svg',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -89,6 +89,7 @@
|
|||||||
"@storybook/react": "~6.5.9",
|
"@storybook/react": "~6.5.9",
|
||||||
"@svgr/webpack": "^6.1.2",
|
"@svgr/webpack": "^6.1.2",
|
||||||
"@swc-node/register": "^1.4.2",
|
"@swc-node/register": "^1.4.2",
|
||||||
|
"@swc/cli": "~0.1.55",
|
||||||
"@swc/core": "^1.2.173",
|
"@swc/core": "^1.2.173",
|
||||||
"@swc/jest": "^0.2.20",
|
"@swc/jest": "^0.2.20",
|
||||||
"@testing-library/react": "13.3.0",
|
"@testing-library/react": "13.3.0",
|
||||||
@ -122,7 +123,7 @@
|
|||||||
"@xstate/react": "^1.6.3",
|
"@xstate/react": "^1.6.3",
|
||||||
"ajv": "^8.11.0",
|
"ajv": "^8.11.0",
|
||||||
"angular": "1.8.0",
|
"angular": "1.8.0",
|
||||||
"autoprefixer": "10.4.8",
|
"autoprefixer": "^10.4.9",
|
||||||
"babel-jest": "28.1.3",
|
"babel-jest": "28.1.3",
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
"chokidar": "^3.5.1",
|
"chokidar": "^3.5.1",
|
||||||
@ -279,9 +280,11 @@
|
|||||||
"@markdoc/markdoc": "0.1.6",
|
"@markdoc/markdoc": "0.1.6",
|
||||||
"@monaco-editor/react": "^4.3.1",
|
"@monaco-editor/react": "^4.3.1",
|
||||||
"@napi-rs/canvas": "^0.1.19",
|
"@napi-rs/canvas": "^0.1.19",
|
||||||
|
"@swc/helpers": "~0.4.11",
|
||||||
"@tailwindcss/aspect-ratio": "^0.4.0",
|
"@tailwindcss/aspect-ratio": "^0.4.0",
|
||||||
"@tailwindcss/forms": "^0.4.0",
|
"@tailwindcss/forms": "^0.4.0",
|
||||||
"@tailwindcss/typography": "^0.5.0",
|
"@tailwindcss/typography": "^0.5.0",
|
||||||
|
"axios": "0.21.1",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"cliui": "^7.0.2",
|
"cliui": "^7.0.2",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
@ -305,12 +308,10 @@
|
|||||||
"string-width": "^4.2.3",
|
"string-width": "^4.2.3",
|
||||||
"tailwindcss": "3.1.8",
|
"tailwindcss": "3.1.8",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"weak-napi": "^2.0.2",
|
"weak-napi": "^2.0.2"
|
||||||
"axios": "0.21.1"
|
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"**/xmlhttprequest-ssl": "~1.6.2",
|
"**/xmlhttprequest-ssl": "~1.6.2",
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,11 +44,10 @@
|
|||||||
"@nrwl/jest": "file:../jest",
|
"@nrwl/jest": "file:../jest",
|
||||||
"@nrwl/linter": "file:../linter",
|
"@nrwl/linter": "file:../linter",
|
||||||
"@nrwl/storybook": "file:../storybook",
|
"@nrwl/storybook": "file:../storybook",
|
||||||
"@nrwl/web": "file:../web",
|
"@nrwl/webpack": "file:../webpack",
|
||||||
"@nrwl/workspace": "file:../workspace",
|
"@nrwl/workspace": "file:../workspace",
|
||||||
"@phenomnomnominal/tsquery": "4.1.1",
|
"@phenomnomnominal/tsquery": "4.1.1",
|
||||||
"@schematics/angular": "~14.2.0",
|
"@schematics/angular": "~14.2.0",
|
||||||
"@typescript-eslint/type-utils": "^5.36.1",
|
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
"chokidar": "^3.5.1",
|
"chokidar": "^3.5.1",
|
||||||
"http-server": "^14.1.0",
|
"http-server": "^14.1.0",
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
parseTargetString,
|
parseTargetString,
|
||||||
readCachedProjectGraph,
|
readCachedProjectGraph,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { WebpackNxBuildCoordinationPlugin } from '@nrwl/web/src/plugins/webpack-nx-build-coordination-plugin';
|
import { WebpackNxBuildCoordinationPlugin } from '@nrwl/webpack/src/plugins/webpack-nx-build-coordination-plugin';
|
||||||
import {
|
import {
|
||||||
calculateProjectDependencies,
|
calculateProjectDependencies,
|
||||||
createTmpTsConfig,
|
createTmpTsConfig,
|
||||||
|
|||||||
@ -2,7 +2,10 @@ import { applyChangesToString, ChangeType, Tree } from '@nrwl/devkit';
|
|||||||
import {
|
import {
|
||||||
__String,
|
__String,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
|
ClassDeclaration,
|
||||||
createSourceFile,
|
createSourceFile,
|
||||||
|
Decorator,
|
||||||
|
getDecorators,
|
||||||
ImportDeclaration,
|
ImportDeclaration,
|
||||||
isArrayLiteralExpression,
|
isArrayLiteralExpression,
|
||||||
isCallExpression,
|
isCallExpression,
|
||||||
@ -18,13 +21,6 @@ import {
|
|||||||
SourceFile,
|
SourceFile,
|
||||||
} from 'typescript';
|
} from 'typescript';
|
||||||
|
|
||||||
/**
|
|
||||||
* Importing this helper from @typescript-eslint/type-utils to ensure
|
|
||||||
* compatibility with TS < 4.8 due to the API change in TS4.8.
|
|
||||||
* This helper allows for support of TS <= 4.8.
|
|
||||||
*/
|
|
||||||
import { getDecorators } from '@typescript-eslint/type-utils';
|
|
||||||
|
|
||||||
type ngModuleDecoratorProperty =
|
type ngModuleDecoratorProperty =
|
||||||
| 'imports'
|
| 'imports'
|
||||||
| 'providers'
|
| 'providers'
|
||||||
@ -57,14 +53,36 @@ export function insertNgModuleProperty(
|
|||||||
|
|
||||||
const ngModuleName = ngModuleNamedImport.name.escapedText;
|
const ngModuleName = ngModuleNamedImport.name.escapedText;
|
||||||
|
|
||||||
const ngModuleClassDeclaration = findDecoratedClass(sourceFile, ngModuleName);
|
/**
|
||||||
|
* Ensure backwards compatibility with TS < 4.8 due to the API change in TS4.8.
|
||||||
const ngModuleDecorator = getDecorators(ngModuleClassDeclaration).find(
|
* The getDecorators util is only in TS 4.8, so we need the previous logic to handle TS < 4.8.
|
||||||
(decorator) =>
|
*
|
||||||
isCallExpression(decorator.expression) &&
|
* TODO: clean this up by removing the requirement to eslint (can we use typescript instead)?
|
||||||
isIdentifier(decorator.expression.expression) &&
|
*/
|
||||||
decorator.expression.expression.escapedText === ngModuleName
|
let ngModuleClassDeclaration: ClassDeclaration;
|
||||||
);
|
let ngModuleDecorator: Decorator;
|
||||||
|
try {
|
||||||
|
ngModuleClassDeclaration = findDecoratedClass(sourceFile, ngModuleName);
|
||||||
|
ngModuleDecorator = getDecorators(ngModuleClassDeclaration).find(
|
||||||
|
(decorator) =>
|
||||||
|
isCallExpression(decorator.expression) &&
|
||||||
|
isIdentifier(decorator.expression.expression) &&
|
||||||
|
decorator.expression.expression.escapedText === ngModuleName
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// Support for TS < 4.8
|
||||||
|
ngModuleClassDeclaration = findDecoratedClassLegacy(
|
||||||
|
sourceFile,
|
||||||
|
ngModuleName
|
||||||
|
);
|
||||||
|
// @ts-ignore
|
||||||
|
ngModuleDecorator = ngModuleClassDeclaration.decorators.find(
|
||||||
|
(decorator) =>
|
||||||
|
isCallExpression(decorator.expression) &&
|
||||||
|
isIdentifier(decorator.expression.expression) &&
|
||||||
|
decorator.expression.expression.escapedText === ngModuleName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const ngModuleCall = ngModuleDecorator.expression as CallExpression;
|
const ngModuleCall = ngModuleDecorator.expression as CallExpression;
|
||||||
|
|
||||||
@ -165,7 +183,10 @@ function getNamedImport(coreImport: ImportDeclaration, importName: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findDecoratedClass(sourceFile: SourceFile, ngModuleName: __String) {
|
function findDecoratedClass(
|
||||||
|
sourceFile: SourceFile,
|
||||||
|
ngModuleName: __String
|
||||||
|
): ClassDeclaration | undefined {
|
||||||
const classDeclarations = sourceFile.statements.filter(isClassDeclaration);
|
const classDeclarations = sourceFile.statements.filter(isClassDeclaration);
|
||||||
return classDeclarations.find((declaration) => {
|
return classDeclarations.find((declaration) => {
|
||||||
const decorators = getDecorators(declaration);
|
const decorators = getDecorators(declaration);
|
||||||
@ -181,6 +202,23 @@ function findDecoratedClass(sourceFile: SourceFile, ngModuleName: __String) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findDecoratedClassLegacy(
|
||||||
|
sourceFile: SourceFile,
|
||||||
|
ngModuleName: __String
|
||||||
|
) {
|
||||||
|
const classDeclarations = sourceFile.statements.filter(isClassDeclaration);
|
||||||
|
return classDeclarations.find(
|
||||||
|
(declaration) =>
|
||||||
|
declaration.decorators &&
|
||||||
|
(declaration.decorators as any[]).some(
|
||||||
|
(decorator) =>
|
||||||
|
isCallExpression(decorator.expression) &&
|
||||||
|
isIdentifier(decorator.expression.expression) &&
|
||||||
|
decorator.expression.expression.escapedText === ngModuleName
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function findPropertyAssignment(
|
function findPropertyAssignment(
|
||||||
ngModuleOptions: ObjectLiteralExpression,
|
ngModuleOptions: ObjectLiteralExpression,
|
||||||
propertyName: ngModuleDecoratorProperty
|
propertyName: ngModuleDecoratorProperty
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
export * from './utils/typescript/load-ts-transformers';
|
||||||
export * from './utils/typescript/print-diagnostics';
|
export * from './utils/typescript/print-diagnostics';
|
||||||
export * from './utils/typescript/run-type-check';
|
export * from './utils/typescript/run-type-check';
|
||||||
export { libraryGenerator } from './generators/library/library';
|
export { libraryGenerator } from './generators/library/library';
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
"@nrwl/jest": "file:../jest",
|
"@nrwl/jest": "file:../jest",
|
||||||
"@nrwl/linter": "file:../linter",
|
"@nrwl/linter": "file:../linter",
|
||||||
"@nrwl/react": "file:../react",
|
"@nrwl/react": "file:../react",
|
||||||
"@nrwl/web": "file:../web",
|
"@nrwl/webpack": "file:../webpack",
|
||||||
"@nrwl/workspace": "file:../workspace",
|
"@nrwl/workspace": "file:../workspace",
|
||||||
"@svgr/webpack": "^6.1.2",
|
"@svgr/webpack": "^6.1.2",
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
|||||||
|
|
||||||
import { PHASE_PRODUCTION_BUILD } from './constants';
|
import { PHASE_PRODUCTION_BUILD } from './constants';
|
||||||
|
|
||||||
jest.mock('@nrwl/web/src/utils/config', () => ({
|
jest.mock('@nrwl/webpack', () => ({
|
||||||
createCopyPlugin: () => {},
|
createCopyPlugin: () => {},
|
||||||
}));
|
}));
|
||||||
jest.mock('tsconfig-paths-webpack-plugin');
|
jest.mock('tsconfig-paths-webpack-plugin');
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
ExecutorContext,
|
ExecutorContext,
|
||||||
offsetFromRoot,
|
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
|
offsetFromRoot,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
// ignoring while we support both Next 11.1.0 and versions before it
|
// ignoring while we support both Next 11.1.0 and versions before it
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -16,18 +16,14 @@ import type {
|
|||||||
import { join, resolve } from 'path';
|
import { join, resolve } from 'path';
|
||||||
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
||||||
import { Configuration } from 'webpack';
|
import { Configuration } from 'webpack';
|
||||||
import {
|
import { FileReplacement, NextBuildBuilderOptions } from './types';
|
||||||
FileReplacement,
|
import { createCopyPlugin, normalizeAssets } from '@nrwl/webpack';
|
||||||
NextBuildBuilderOptions,
|
|
||||||
WebpackConfigOptions,
|
|
||||||
} from './types';
|
|
||||||
import { normalizeAssets } from '@nrwl/web/src/utils/normalize';
|
|
||||||
import { createCopyPlugin } from '@nrwl/web/src/utils/config';
|
|
||||||
import { WithNxOptions } from '../../plugins/with-nx';
|
import { WithNxOptions } from '../../plugins/with-nx';
|
||||||
import {
|
import {
|
||||||
createTmpTsConfig,
|
createTmpTsConfig,
|
||||||
DependentBuildableProjectNode,
|
DependentBuildableProjectNode,
|
||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
|
|
||||||
const loadConfig = require('next/dist/server/config').default;
|
const loadConfig = require('next/dist/server/config').default;
|
||||||
|
|
||||||
export function createWebpackConfig(
|
export function createWebpackConfig(
|
||||||
|
|||||||
@ -33,6 +33,12 @@
|
|||||||
"version": "13.8.5-beta.1",
|
"version": "13.8.5-beta.1",
|
||||||
"description": "Renames @nrwl/node:package to @nrwl/js:tsc",
|
"description": "Renames @nrwl/node:package to @nrwl/js:tsc",
|
||||||
"factory": "./src/migrations/update-13-8-5/update-package-to-tsc"
|
"factory": "./src/migrations/update-13-8-5/update-package-to-tsc"
|
||||||
|
},
|
||||||
|
"update-webpack-executor": {
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "14.7.5-beta.1",
|
||||||
|
"description": "Update usages of webpack executors to @nrwl/webpack",
|
||||||
|
"factory": "./src/migrations/update-14-7-5/update-webpack-executor"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageJsonUpdates": {
|
"packageJsonUpdates": {
|
||||||
|
|||||||
@ -34,26 +34,16 @@
|
|||||||
"@nrwl/jest": "file:../jest",
|
"@nrwl/jest": "file:../jest",
|
||||||
"@nrwl/js": "file:../js",
|
"@nrwl/js": "file:../js",
|
||||||
"@nrwl/linter": "file:../linter",
|
"@nrwl/linter": "file:../linter",
|
||||||
|
"@nrwl/webpack": "file:../webpack",
|
||||||
"@nrwl/workspace": "file:../workspace",
|
"@nrwl/workspace": "file:../workspace",
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
"copy-webpack-plugin": "^10.2.4",
|
|
||||||
"dotenv": "~10.0.0",
|
"dotenv": "~10.0.0",
|
||||||
"enhanced-resolve": "^5.8.3",
|
|
||||||
"fork-ts-checker-webpack-plugin": "7.2.13",
|
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"glob": "7.1.4",
|
"glob": "7.1.4",
|
||||||
"license-webpack-plugin": "^4.0.2",
|
|
||||||
"rxjs": "^6.5.4",
|
"rxjs": "^6.5.4",
|
||||||
"source-map-support": "0.5.19",
|
|
||||||
"terser-webpack-plugin": "^5.3.3",
|
|
||||||
"tree-kill": "1.2.2",
|
"tree-kill": "1.2.2",
|
||||||
"ts-loader": "^9.3.1",
|
|
||||||
"ts-node": "10.9.1",
|
"ts-node": "10.9.1",
|
||||||
"tsconfig-paths": "^3.9.0",
|
"tsconfig-paths": "^3.9.0",
|
||||||
"tsconfig-paths-webpack-plugin": "3.5.2",
|
"tslib": "^2.3.0"
|
||||||
"tslib": "^2.3.0",
|
|
||||||
"webpack": "^5.58.1",
|
|
||||||
"webpack-merge": "^5.8.0",
|
|
||||||
"webpack-node-externals": "^3.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { ExecutorContext } from '@nrwl/devkit';
|
|||||||
import type { NodeExecutorOptions } from '@nrwl/js/src/executors/node/schema';
|
import type { NodeExecutorOptions } from '@nrwl/js/src/executors/node/schema';
|
||||||
import { nodeExecutor as jsNodeExecutor } from '@nrwl/js/src/executors/node/node.impl';
|
import { nodeExecutor as jsNodeExecutor } from '@nrwl/js/src/executors/node/node.impl';
|
||||||
|
|
||||||
// TODO(jack): Remove for Nx 15
|
// TODO(jack): Remove for Nx 16
|
||||||
export async function* nodeExecutor(
|
export async function* nodeExecutor(
|
||||||
options: NodeExecutorOptions,
|
options: NodeExecutorOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
|
|||||||
@ -1,162 +0,0 @@
|
|||||||
import { ExecutorContext } from '@nrwl/devkit';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import * as projectGraph from '@nrwl/devkit';
|
|
||||||
import type { ProjectGraph } from '@nrwl/devkit';
|
|
||||||
import webpackExecutor from './webpack.impl';
|
|
||||||
import { BuildNodeBuilderOptions } from '../../utils/types';
|
|
||||||
|
|
||||||
jest.mock('tsconfig-paths-webpack-plugin');
|
|
||||||
jest.mock('../../utils/run-webpack', () => ({
|
|
||||||
runWebpack: jest.fn(),
|
|
||||||
}));
|
|
||||||
import { runWebpack } from '../../utils/run-webpack';
|
|
||||||
|
|
||||||
describe('Node Build Executor', () => {
|
|
||||||
let context: ExecutorContext;
|
|
||||||
let options: BuildNodeBuilderOptions;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
(<any>runWebpack).mockReturnValue(of({ hasErrors: () => false }));
|
|
||||||
context = {
|
|
||||||
root: '/root',
|
|
||||||
cwd: '/root',
|
|
||||||
projectName: 'my-app',
|
|
||||||
targetName: 'build',
|
|
||||||
workspace: {
|
|
||||||
version: 2,
|
|
||||||
projects: {
|
|
||||||
'my-app': <any>{
|
|
||||||
root: 'apps/wibble',
|
|
||||||
sourceRoot: 'apps/wibble',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
npmScope: 'test',
|
|
||||||
},
|
|
||||||
projectGraph: {} as ProjectGraph,
|
|
||||||
isVerbose: false,
|
|
||||||
};
|
|
||||||
options = {
|
|
||||||
outputPath: 'dist/apps/wibble',
|
|
||||||
externalDependencies: 'all',
|
|
||||||
main: 'apps/wibble/src/main.ts',
|
|
||||||
tsConfig: 'apps/wibble/tsconfig.ts',
|
|
||||||
buildLibsFromSource: true,
|
|
||||||
fileReplacements: [],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => jest.clearAllMocks());
|
|
||||||
|
|
||||||
it('should call webpack', async () => {
|
|
||||||
await webpackExecutor(options, context).next();
|
|
||||||
|
|
||||||
expect(runWebpack).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
output: expect.objectContaining({
|
|
||||||
filename: 'main.js',
|
|
||||||
libraryTarget: 'commonjs',
|
|
||||||
path: '/root/dist/apps/wibble',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use outputFileName if passed in', async () => {
|
|
||||||
await webpackExecutor(
|
|
||||||
{ ...options, outputFileName: 'index.js' },
|
|
||||||
context
|
|
||||||
).next();
|
|
||||||
|
|
||||||
expect(runWebpack).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
entry: expect.objectContaining({
|
|
||||||
index: ['/root/apps/wibble/src/main.ts'],
|
|
||||||
}),
|
|
||||||
output: expect.objectContaining({
|
|
||||||
filename: 'index.js',
|
|
||||||
libraryTarget: 'commonjs',
|
|
||||||
path: '/root/dist/apps/wibble',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use watchOptions if passed in', async () => {
|
|
||||||
await webpackExecutor(
|
|
||||||
{
|
|
||||||
...options,
|
|
||||||
watchOptions: {
|
|
||||||
ignored: ['path1'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
context
|
|
||||||
).next();
|
|
||||||
|
|
||||||
expect(runWebpack).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
watchOptions: {
|
|
||||||
ignored: ['path1'],
|
|
||||||
aggregateTimeout: 200,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('webpackConfig', () => {
|
|
||||||
it('should handle custom path', async () => {
|
|
||||||
jest.mock(
|
|
||||||
'/root/config.js',
|
|
||||||
() => (options) => ({ ...options, prop: 'my-val' }),
|
|
||||||
{ virtual: true }
|
|
||||||
);
|
|
||||||
await webpackExecutor(
|
|
||||||
{ ...options, webpackConfig: 'config.js' },
|
|
||||||
context
|
|
||||||
).next();
|
|
||||||
|
|
||||||
expect(runWebpack).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
output: expect.objectContaining({
|
|
||||||
filename: 'main.js',
|
|
||||||
libraryTarget: 'commonjs',
|
|
||||||
path: '/root/dist/apps/wibble',
|
|
||||||
}),
|
|
||||||
prop: 'my-val',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle multiple custom paths in order', async () => {
|
|
||||||
jest.mock(
|
|
||||||
'/root/config1.js',
|
|
||||||
() => (o) => ({ ...o, prop1: 'my-val-1' }),
|
|
||||||
{ virtual: true }
|
|
||||||
);
|
|
||||||
jest.mock(
|
|
||||||
'/root/config2.js',
|
|
||||||
() => (o) => ({
|
|
||||||
...o,
|
|
||||||
prop1: o.prop1 + '-my-val-2',
|
|
||||||
prop2: 'my-val-2',
|
|
||||||
}),
|
|
||||||
{ virtual: true }
|
|
||||||
);
|
|
||||||
await webpackExecutor(
|
|
||||||
{ ...options, webpackConfig: ['config1.js', 'config2.js'] },
|
|
||||||
context
|
|
||||||
).next();
|
|
||||||
|
|
||||||
expect(runWebpack).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
output: expect.objectContaining({
|
|
||||||
filename: 'main.js',
|
|
||||||
libraryTarget: 'commonjs',
|
|
||||||
path: '/root/dist/apps/wibble',
|
|
||||||
}),
|
|
||||||
prop1: 'my-val-1-my-val-2',
|
|
||||||
prop2: 'my-val-2',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,109 +1,23 @@
|
|||||||
import 'dotenv/config';
|
/**
|
||||||
|
* For backwards compat.
|
||||||
|
* TODO(jack): Remove in Nx 16.
|
||||||
|
*/
|
||||||
import { ExecutorContext } from '@nrwl/devkit';
|
import { ExecutorContext } from '@nrwl/devkit';
|
||||||
import { eachValueFrom } from '@nrwl/devkit/src/utils/rxjs-for-await';
|
import type { WebpackExecutorOptions } from '@nrwl/webpack';
|
||||||
import {
|
import { webpackExecutor as baseWebpackExecutor } from '@nrwl/webpack';
|
||||||
calculateProjectDependencies,
|
|
||||||
createTmpTsConfig,
|
|
||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
|
||||||
import { getRootTsConfigPath } from '@nrwl/workspace/src/utilities/typescript';
|
|
||||||
|
|
||||||
import { map, tap } from 'rxjs/operators';
|
|
||||||
import { resolve } from 'path';
|
|
||||||
import { register } from 'ts-node';
|
|
||||||
|
|
||||||
import { getNodeWebpackConfig } from '../../utils/node.config';
|
|
||||||
import { BuildNodeBuilderOptions } from '../../utils/types';
|
|
||||||
import { normalizeBuildOptions } from '../../utils/normalize';
|
|
||||||
import { deleteOutputDir } from '../../utils/fs';
|
|
||||||
import { runWebpack } from '../../utils/run-webpack';
|
|
||||||
|
|
||||||
export type NodeBuildEvent = {
|
|
||||||
outfile: string;
|
|
||||||
success: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function* webpackExecutor(
|
export async function* webpackExecutor(
|
||||||
rawOptions: BuildNodeBuilderOptions,
|
options: WebpackExecutorOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
) {
|
) {
|
||||||
const { sourceRoot, root } = context.workspace.projects[context.projectName];
|
yield* baseWebpackExecutor(
|
||||||
|
{
|
||||||
if (!sourceRoot) {
|
...options,
|
||||||
throw new Error(`${context.projectName} does not have a sourceRoot.`);
|
target: 'node',
|
||||||
}
|
compiler: 'tsc',
|
||||||
|
|
||||||
if (!root) {
|
|
||||||
throw new Error(`${context.projectName} does not have a root.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = normalizeBuildOptions(
|
|
||||||
rawOptions,
|
|
||||||
context.root,
|
|
||||||
sourceRoot,
|
|
||||||
root
|
|
||||||
);
|
|
||||||
|
|
||||||
if (options.webpackConfig.some((x) => x.endsWith('.ts'))) {
|
|
||||||
registerTsNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.buildLibsFromSource) {
|
|
||||||
const { target, dependencies } = calculateProjectDependencies(
|
|
||||||
context.projectGraph,
|
|
||||||
context.root,
|
|
||||||
context.projectName,
|
|
||||||
context.targetName,
|
|
||||||
context.configurationName
|
|
||||||
);
|
|
||||||
options.tsConfig = createTmpTsConfig(
|
|
||||||
options.tsConfig,
|
|
||||||
context.root,
|
|
||||||
target.data.root,
|
|
||||||
dependencies
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete output path before bundling
|
|
||||||
if (options.deleteOutputPath) {
|
|
||||||
deleteOutputDir(context.root, options.outputPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = await options.webpackConfig.reduce(
|
|
||||||
async (currentConfig, plugin) => {
|
|
||||||
return require(plugin)(await currentConfig, {
|
|
||||||
options,
|
|
||||||
configuration: context.configurationName,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
Promise.resolve(
|
context
|
||||||
getNodeWebpackConfig(context, context.projectGraph, options)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return yield* eachValueFrom(
|
|
||||||
runWebpack(config).pipe(
|
|
||||||
tap((stats) => {
|
|
||||||
console.info(stats.toString(config.stats));
|
|
||||||
}),
|
|
||||||
map((stats) => {
|
|
||||||
return {
|
|
||||||
success: !stats.hasErrors(),
|
|
||||||
outfile: resolve(
|
|
||||||
context.root,
|
|
||||||
options.outputPath,
|
|
||||||
options.outputFileName
|
|
||||||
),
|
|
||||||
} as NodeBuildEvent;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerTsNode() {
|
|
||||||
const rootTsConfig = getRootTsConfigPath();
|
|
||||||
register({
|
|
||||||
...(rootTsConfig ? { project: rootTsConfig } : null),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default webpackExecutor;
|
export default webpackExecutor;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { NxJsonConfiguration, readJson, Tree, getProjects } from '@nrwl/devkit';
|
|
||||||
import * as devkit from '@nrwl/devkit';
|
import * as devkit from '@nrwl/devkit';
|
||||||
|
import { getProjects, NxJsonConfiguration, readJson, Tree } from '@nrwl/devkit';
|
||||||
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
|
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
|
||||||
|
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
@ -44,9 +44,11 @@ describe('app', () => {
|
|||||||
expect(project.architect).toEqual(
|
expect(project.architect).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
build: {
|
build: {
|
||||||
builder: '@nrwl/node:webpack',
|
builder: '@nrwl/webpack:webpack',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
options: {
|
options: {
|
||||||
|
target: 'node',
|
||||||
|
compiler: 'tsc',
|
||||||
outputPath: 'dist/apps/my-node-app',
|
outputPath: 'dist/apps/my-node-app',
|
||||||
main: 'apps/my-node-app/src/main.ts',
|
main: 'apps/my-node-app/src/main.ts',
|
||||||
tsConfig: 'apps/my-node-app/tsconfig.app.json',
|
tsConfig: 'apps/my-node-app/tsconfig.app.json',
|
||||||
@ -67,7 +69,7 @@ describe('app', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
serve: {
|
serve: {
|
||||||
builder: '@nrwl/node:node',
|
builder: '@nrwl/js:node',
|
||||||
options: {
|
options: {
|
||||||
buildTarget: 'my-node-app:build',
|
buildTarget: 'my-node-app:build',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -40,9 +40,11 @@ function getBuildConfig(
|
|||||||
options: NormalizedSchema
|
options: NormalizedSchema
|
||||||
): TargetConfiguration {
|
): TargetConfiguration {
|
||||||
return {
|
return {
|
||||||
executor: '@nrwl/node:webpack',
|
executor: '@nrwl/webpack:webpack',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
options: {
|
options: {
|
||||||
|
target: 'node',
|
||||||
|
compiler: 'tsc',
|
||||||
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||||
main: joinPathFragments(
|
main: joinPathFragments(
|
||||||
project.sourceRoot,
|
project.sourceRoot,
|
||||||
@ -75,7 +77,7 @@ function getBuildConfig(
|
|||||||
|
|
||||||
function getServeConfig(options: NormalizedSchema): TargetConfiguration {
|
function getServeConfig(options: NormalizedSchema): TargetConfiguration {
|
||||||
return {
|
return {
|
||||||
executor: '@nrwl/node:node',
|
executor: '@nrwl/js:node',
|
||||||
options: {
|
options: {
|
||||||
buildTarget: `${options.name}:build`,
|
buildTarget: `${options.name}:build`,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
import { readJson } from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
|
||||||
|
|
||||||
|
import update from './update-webpack-executor';
|
||||||
|
|
||||||
|
describe('Migration: @nrwl/webpack', () => {
|
||||||
|
it(`should update usage of webpack executor`, async () => {
|
||||||
|
let tree = createTreeWithEmptyV1Workspace();
|
||||||
|
|
||||||
|
tree.write(
|
||||||
|
'workspace.json',
|
||||||
|
JSON.stringify({
|
||||||
|
version: 2,
|
||||||
|
projects: {
|
||||||
|
myapp: {
|
||||||
|
root: 'apps/myapp',
|
||||||
|
sourceRoot: 'apps/myapp/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nrwl/node:webpack',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await update(tree);
|
||||||
|
|
||||||
|
expect(readJson(tree, 'workspace.json')).toEqual({
|
||||||
|
version: 2,
|
||||||
|
projects: {
|
||||||
|
myapp: {
|
||||||
|
root: 'apps/myapp',
|
||||||
|
sourceRoot: 'apps/myapp/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nrwl/webpack:webpack',
|
||||||
|
options: {
|
||||||
|
compiler: 'tsc',
|
||||||
|
target: 'node',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import {
|
||||||
|
formatFiles,
|
||||||
|
getProjects,
|
||||||
|
Tree,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export default async function update(host: Tree) {
|
||||||
|
const projects = getProjects(host);
|
||||||
|
|
||||||
|
for (const [name, config] of projects.entries()) {
|
||||||
|
if (config?.targets?.build?.executor === '@nrwl/node:webpack') {
|
||||||
|
config.targets.build.executor = '@nrwl/webpack:webpack';
|
||||||
|
config.targets.build.options.target = 'node';
|
||||||
|
config.targets.build.options.compiler = 'tsc';
|
||||||
|
updateProjectConfiguration(host, name, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await formatFiles(host);
|
||||||
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
export const before = () => {};
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export const after = () => {};
|
|
||||||
@ -1,222 +0,0 @@
|
|||||||
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
|
||||||
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
|
||||||
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
import type { Configuration, WebpackPluginInstance } from 'webpack';
|
|
||||||
import * as webpack from 'webpack';
|
|
||||||
import { loadTsTransformers } from './load-ts-transformers';
|
|
||||||
import { BuildBuilderOptions } from './types';
|
|
||||||
import CopyWebpackPlugin = require('copy-webpack-plugin');
|
|
||||||
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
|
||||||
import { removeExt } from '@nrwl/workspace/src/utils/runtime-lint-utils';
|
|
||||||
|
|
||||||
export const OUT_FILENAME_TEMPLATE = '[name].js';
|
|
||||||
|
|
||||||
export function getBaseWebpackPartial(
|
|
||||||
options: BuildBuilderOptions
|
|
||||||
): Configuration {
|
|
||||||
const { options: compilerOptions } = readTsConfig(options.tsConfig);
|
|
||||||
const supportsEs2015 =
|
|
||||||
compilerOptions.target !== ts.ScriptTarget.ES3 &&
|
|
||||||
compilerOptions.target !== ts.ScriptTarget.ES5;
|
|
||||||
const mainFields = [...(supportsEs2015 ? ['es2015'] : []), 'module', 'main'];
|
|
||||||
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
|
||||||
|
|
||||||
const { compilerPluginHooks, hasPlugin } = loadTsTransformers(
|
|
||||||
options.transformers
|
|
||||||
);
|
|
||||||
|
|
||||||
const additionalEntryPoints =
|
|
||||||
options.additionalEntryPoints?.reduce(
|
|
||||||
(obj, current) => ({
|
|
||||||
...obj,
|
|
||||||
[current.entryName]: current.entryPath,
|
|
||||||
}),
|
|
||||||
{} as { [entryName: string]: string }
|
|
||||||
) ?? {};
|
|
||||||
const mainEntry = options.outputFileName
|
|
||||||
? removeExt(options.outputFileName)
|
|
||||||
: 'main';
|
|
||||||
const webpackConfig: Configuration = {
|
|
||||||
entry: {
|
|
||||||
[mainEntry]: [options.main],
|
|
||||||
...additionalEntryPoints,
|
|
||||||
},
|
|
||||||
devtool: options.sourceMap ? 'source-map' : false,
|
|
||||||
mode: options.optimization ? 'production' : 'development',
|
|
||||||
output: {
|
|
||||||
path: options.outputPath,
|
|
||||||
filename:
|
|
||||||
options.additionalEntryPoints?.length > 0
|
|
||||||
? OUT_FILENAME_TEMPLATE
|
|
||||||
: options.outputFileName,
|
|
||||||
hashFunction: 'xxhash64',
|
|
||||||
// Disabled for performance
|
|
||||||
pathinfo: false,
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
// Enabled for performance
|
|
||||||
unsafeCache: true,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.([jt])sx?$/,
|
|
||||||
loader: require.resolve(`ts-loader`),
|
|
||||||
exclude: /node_modules/,
|
|
||||||
options: {
|
|
||||||
configFile: options.tsConfig,
|
|
||||||
transpileOnly: !hasPlugin,
|
|
||||||
// https://github.com/TypeStrong/ts-loader/pull/685
|
|
||||||
experimentalWatchApi: true,
|
|
||||||
getCustomTransformers: (program) => ({
|
|
||||||
before: compilerPluginHooks.beforeHooks.map((hook) =>
|
|
||||||
hook(program)
|
|
||||||
),
|
|
||||||
after: compilerPluginHooks.afterHooks.map((hook) =>
|
|
||||||
hook(program)
|
|
||||||
),
|
|
||||||
afterDeclarations: compilerPluginHooks.afterDeclarationsHooks.map(
|
|
||||||
(hook) => hook(program)
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions,
|
|
||||||
alias: getAliases(options),
|
|
||||||
plugins: [
|
|
||||||
new TsconfigPathsPlugin({
|
|
||||||
configFile: options.tsConfig,
|
|
||||||
extensions,
|
|
||||||
mainFields,
|
|
||||||
}) as never, // TODO: Remove never type when 'tsconfig-paths-webpack-plugin' types fixed
|
|
||||||
],
|
|
||||||
mainFields,
|
|
||||||
},
|
|
||||||
performance: {
|
|
||||||
hints: false,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new ForkTsCheckerWebpackPlugin({
|
|
||||||
// For watch mode, type errors should result in failure.
|
|
||||||
async: false,
|
|
||||||
typescript: {
|
|
||||||
configFile: options.tsConfig,
|
|
||||||
memoryLimit: options.memoryLimit || 2018,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
watch: options.watch,
|
|
||||||
watchOptions: {
|
|
||||||
// Delay the next rebuild from first file change, otherwise can lead to
|
|
||||||
// two builds on a single file change.
|
|
||||||
aggregateTimeout: 200,
|
|
||||||
poll: options.poll,
|
|
||||||
...options.watchOptions,
|
|
||||||
},
|
|
||||||
stats: getStatsConfig(options),
|
|
||||||
experiments: {
|
|
||||||
cacheUnaffected: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const extraPlugins: WebpackPluginInstance[] = [];
|
|
||||||
|
|
||||||
if (options.progress) {
|
|
||||||
extraPlugins.push(new webpack.ProgressPlugin());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.extractLicenses) {
|
|
||||||
extraPlugins.push(
|
|
||||||
new LicenseWebpackPlugin({
|
|
||||||
stats: {
|
|
||||||
errors: false,
|
|
||||||
},
|
|
||||||
perChunkOutput: false,
|
|
||||||
outputFilename: `3rdpartylicenses.txt`,
|
|
||||||
}) as unknown as WebpackPluginInstance
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// process asset entries
|
|
||||||
if (Array.isArray(options.assets) && options.assets.length > 0) {
|
|
||||||
const copyWebpackPluginInstance = new CopyWebpackPlugin({
|
|
||||||
patterns: options.assets.map((asset) => {
|
|
||||||
return {
|
|
||||||
context: asset.input,
|
|
||||||
// Now we remove starting slash to make Webpack place it from the output root.
|
|
||||||
to: asset.output,
|
|
||||||
from: asset.glob,
|
|
||||||
globOptions: {
|
|
||||||
ignore: [
|
|
||||||
'.gitkeep',
|
|
||||||
'**/.DS_Store',
|
|
||||||
'**/Thumbs.db',
|
|
||||||
...(asset.ignore ?? []),
|
|
||||||
],
|
|
||||||
dot: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
new CopyWebpackPlugin({
|
|
||||||
patterns: options.assets.map((asset: any) => {
|
|
||||||
return {
|
|
||||||
context: asset.input,
|
|
||||||
// Now we remove starting slash to make Webpack place it from the output root.
|
|
||||||
to: asset.output,
|
|
||||||
from: asset.glob,
|
|
||||||
globOptions: {
|
|
||||||
ignore: [
|
|
||||||
'.gitkeep',
|
|
||||||
'**/.DS_Store',
|
|
||||||
'**/Thumbs.db',
|
|
||||||
...(asset.ignore ?? []),
|
|
||||||
],
|
|
||||||
dot: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
extraPlugins.push(copyWebpackPluginInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
webpackConfig.plugins = [...webpackConfig.plugins, ...extraPlugins];
|
|
||||||
|
|
||||||
return webpackConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAliases(options: BuildBuilderOptions): { [key: string]: string } {
|
|
||||||
return options.fileReplacements.reduce(
|
|
||||||
(aliases, replacement) => ({
|
|
||||||
...aliases,
|
|
||||||
[replacement.replace]: replacement.with,
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStatsConfig(options: BuildBuilderOptions) {
|
|
||||||
return {
|
|
||||||
hash: true,
|
|
||||||
timings: false,
|
|
||||||
cached: false,
|
|
||||||
cachedAssets: false,
|
|
||||||
modules: false,
|
|
||||||
warnings: true,
|
|
||||||
errors: true,
|
|
||||||
colors: !options.verbose && !options.statsJson,
|
|
||||||
chunks: !options.verbose,
|
|
||||||
assets: !!options.verbose,
|
|
||||||
chunkOrigins: !!options.verbose,
|
|
||||||
chunkModules: !!options.verbose,
|
|
||||||
children: !!options.verbose,
|
|
||||||
reasons: !!options.verbose,
|
|
||||||
version: !!options.verbose,
|
|
||||||
errorDetails: !!options.verbose,
|
|
||||||
moduleTrace: !!options.verbose,
|
|
||||||
usedExports: !!options.verbose,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { loadTsTransformers } from './load-ts-transformers';
|
|
||||||
|
|
||||||
jest.mock('plugin-a');
|
|
||||||
jest.mock('plugin-b');
|
|
||||||
const mockRequireResolve = jest.fn((path) => path);
|
|
||||||
|
|
||||||
describe('loadTsTransformers', () => {
|
|
||||||
it('should return empty hooks if plugins is falsy', () => {
|
|
||||||
const result = loadTsTransformers(undefined);
|
|
||||||
assertEmptyResult(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return empty hooks if plugins is []', () => {
|
|
||||||
const result = loadTsTransformers([]);
|
|
||||||
assertEmptyResult(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return correct compiler hooks', () => {
|
|
||||||
const result = loadTsTransformers(
|
|
||||||
['plugin-a', 'plugin-b'],
|
|
||||||
mockRequireResolve as any
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.hasPlugin).toEqual(true);
|
|
||||||
expect(result.compilerPluginHooks).toEqual({
|
|
||||||
beforeHooks: [expect.any(Function)],
|
|
||||||
afterHooks: [expect.any(Function)],
|
|
||||||
afterDeclarationsHooks: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function assertEmptyResult(result: ReturnType<typeof loadTsTransformers>) {
|
|
||||||
expect(result.hasPlugin).toEqual(false);
|
|
||||||
expect(result.compilerPluginHooks).toEqual({
|
|
||||||
beforeHooks: [],
|
|
||||||
afterHooks: [],
|
|
||||||
afterDeclarationsHooks: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
import { logger } from '@nrwl/devkit';
|
|
||||||
import { join } from 'path';
|
|
||||||
import {
|
|
||||||
CompilerPlugin,
|
|
||||||
CompilerPluginHooks,
|
|
||||||
TransformerEntry,
|
|
||||||
TransformerPlugin,
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
export function loadTsTransformers(
|
|
||||||
plugins: TransformerEntry[],
|
|
||||||
moduleResolver: typeof require.resolve = require.resolve
|
|
||||||
): {
|
|
||||||
compilerPluginHooks: CompilerPluginHooks;
|
|
||||||
hasPlugin: boolean;
|
|
||||||
} {
|
|
||||||
const beforeHooks: CompilerPluginHooks['beforeHooks'] = [];
|
|
||||||
const afterHooks: CompilerPluginHooks['afterHooks'] = [];
|
|
||||||
const afterDeclarationsHooks: CompilerPluginHooks['afterDeclarationsHooks'] =
|
|
||||||
[];
|
|
||||||
|
|
||||||
if (!plugins || !plugins.length)
|
|
||||||
return {
|
|
||||||
compilerPluginHooks: {
|
|
||||||
beforeHooks,
|
|
||||||
afterHooks,
|
|
||||||
afterDeclarationsHooks,
|
|
||||||
},
|
|
||||||
hasPlugin: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizedPlugins: TransformerPlugin[] = plugins.map((plugin) =>
|
|
||||||
typeof plugin === 'string' ? { name: plugin, options: {} } : plugin
|
|
||||||
);
|
|
||||||
|
|
||||||
const nodeModulePaths = [
|
|
||||||
join(process.cwd(), 'node_modules'),
|
|
||||||
...module.paths,
|
|
||||||
];
|
|
||||||
|
|
||||||
const pluginRefs: CompilerPlugin[] = normalizedPlugins.map(({ name }) => {
|
|
||||||
try {
|
|
||||||
const binaryPath = moduleResolver(name, {
|
|
||||||
paths: nodeModulePaths,
|
|
||||||
});
|
|
||||||
return require(binaryPath);
|
|
||||||
} catch (e) {
|
|
||||||
logger.warn(`"${name}" plugin could not be found!`);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let i = 0; i < pluginRefs.length; i++) {
|
|
||||||
const { name: pluginName, options: pluginOptions } = normalizedPlugins[i];
|
|
||||||
const { before, after, afterDeclarations } = pluginRefs[i];
|
|
||||||
if (!before && !after && !afterDeclarations) {
|
|
||||||
logger.warn(
|
|
||||||
`${pluginName} is not a Transformer Plugin. It does not provide neither before(), after(), nor afterDeclarations()`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (before) {
|
|
||||||
beforeHooks.push(before.bind(before, pluginOptions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (after) {
|
|
||||||
afterHooks.push(after.bind(after, pluginOptions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (afterDeclarations) {
|
|
||||||
afterDeclarationsHooks.push(
|
|
||||||
afterDeclarations.bind(afterDeclarations, pluginOptions)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
compilerPluginHooks: {
|
|
||||||
beforeHooks,
|
|
||||||
afterHooks,
|
|
||||||
afterDeclarationsHooks,
|
|
||||||
},
|
|
||||||
hasPlugin: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,137 +0,0 @@
|
|||||||
import { ProjectGraph, ExecutorContext } from '@nrwl/devkit';
|
|
||||||
import { join } from 'path';
|
|
||||||
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
|
||||||
|
|
||||||
import { GeneratePackageJsonWebpackPlugin } from './generate-package-json-webpack-plugin';
|
|
||||||
import { getNodeWebpackConfig } from './node.config';
|
|
||||||
import { BuildNodeBuilderOptions } from './types';
|
|
||||||
|
|
||||||
jest.mock('tsconfig-paths-webpack-plugin');
|
|
||||||
jest.mock('@nrwl/devkit', () => ({
|
|
||||||
get workspaceRoot() {
|
|
||||||
return join(__dirname, '../../../..');
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('getNodePartial', () => {
|
|
||||||
let context: ExecutorContext;
|
|
||||||
let projectGraph: ProjectGraph;
|
|
||||||
let input: BuildNodeBuilderOptions;
|
|
||||||
beforeEach(() => {
|
|
||||||
context = {
|
|
||||||
projectName: 'sample-project',
|
|
||||||
root: '/root',
|
|
||||||
cwd: '',
|
|
||||||
target: {} as any,
|
|
||||||
isVerbose: false,
|
|
||||||
workspace: {} as any,
|
|
||||||
};
|
|
||||||
projectGraph = {} as any;
|
|
||||||
input = {
|
|
||||||
main: 'main.ts',
|
|
||||||
outputPath: 'dist',
|
|
||||||
tsConfig: 'tsconfig.json',
|
|
||||||
externalDependencies: 'all',
|
|
||||||
fileReplacements: [],
|
|
||||||
statsJson: false,
|
|
||||||
};
|
|
||||||
(<any>TsconfigPathsPlugin).mockImplementation(
|
|
||||||
function MockPathsPlugin() {}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('unconditionally', () => {
|
|
||||||
it('should target commonjs', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, input);
|
|
||||||
expect(result.output.libraryTarget).toEqual('commonjs');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should target node', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, input);
|
|
||||||
|
|
||||||
expect(result.target).toEqual('node');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not polyfill node apis', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, input);
|
|
||||||
|
|
||||||
expect(result.node).toEqual(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('the optimization option when true', () => {
|
|
||||||
it('should minify', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, {
|
|
||||||
...input,
|
|
||||||
optimization: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.optimization.minimize).toEqual(true);
|
|
||||||
expect(result.optimization.minimizer).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should concatenate modules', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, {
|
|
||||||
...input,
|
|
||||||
optimization: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.optimization.concatenateModules).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('the externalDependencies option', () => {
|
|
||||||
it('should change all node_modules to commonjs imports', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, input);
|
|
||||||
const callback = jest.fn();
|
|
||||||
result.externals[0](null, '@nestjs/core', callback);
|
|
||||||
expect(callback).toHaveBeenCalledWith(null, 'commonjs @nestjs/core');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should change given module names to commonjs imports but not others', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, {
|
|
||||||
...input,
|
|
||||||
externalDependencies: ['module1'],
|
|
||||||
});
|
|
||||||
const callback = jest.fn();
|
|
||||||
result.externals[0]({ request: 'module1' }, callback);
|
|
||||||
expect(callback).toHaveBeenCalledWith(null, 'commonjs module1');
|
|
||||||
result.externals[0]({ request: '@nestjs/core' }, callback);
|
|
||||||
expect(callback).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change any modules to commonjs imports', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, {
|
|
||||||
...input,
|
|
||||||
externalDependencies: 'none',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.externals).not.toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('the generatePackageJson option', () => {
|
|
||||||
it('should add the GeneratePackageJsonWebpackPlugin plugin', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, {
|
|
||||||
...input,
|
|
||||||
generatePackageJson: true,
|
|
||||||
});
|
|
||||||
expect(
|
|
||||||
result.plugins.find(
|
|
||||||
(plugin) => plugin instanceof GeneratePackageJsonWebpackPlugin
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
|
||||||
it('should not add the GeneratePackageJsonWebpackPlugin plugin', () => {
|
|
||||||
const result = getNodeWebpackConfig(context, projectGraph, {
|
|
||||||
...input,
|
|
||||||
generatePackageJson: false,
|
|
||||||
});
|
|
||||||
expect(
|
|
||||||
result.plugins.find(
|
|
||||||
(plugin) => plugin instanceof GeneratePackageJsonWebpackPlugin
|
|
||||||
)
|
|
||||||
).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
import { ExecutorContext, ProjectGraph, workspaceRoot } from '@nrwl/devkit';
|
|
||||||
import { Configuration } from 'webpack';
|
|
||||||
import { merge } from 'webpack-merge';
|
|
||||||
|
|
||||||
import { getBaseWebpackPartial } from './config';
|
|
||||||
import { GeneratePackageJsonWebpackPlugin } from './generate-package-json-webpack-plugin';
|
|
||||||
import { BuildNodeBuilderOptions } from './types';
|
|
||||||
import nodeExternals = require('webpack-node-externals');
|
|
||||||
import TerserPlugin = require('terser-webpack-plugin');
|
|
||||||
|
|
||||||
function getNodePartial(
|
|
||||||
context: ExecutorContext,
|
|
||||||
projectGraph: ProjectGraph,
|
|
||||||
options: BuildNodeBuilderOptions
|
|
||||||
) {
|
|
||||||
const webpackConfig: Configuration = {
|
|
||||||
output: {
|
|
||||||
libraryTarget: 'commonjs',
|
|
||||||
},
|
|
||||||
target: 'node',
|
|
||||||
node: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (options.optimization) {
|
|
||||||
webpackConfig.optimization = {
|
|
||||||
minimize: true,
|
|
||||||
minimizer: [
|
|
||||||
new TerserPlugin({
|
|
||||||
terserOptions: {
|
|
||||||
mangle: false,
|
|
||||||
keep_classnames: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
concatenateModules: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.externalDependencies === 'all') {
|
|
||||||
const modulesDir = `${workspaceRoot}/node_modules`;
|
|
||||||
webpackConfig.externals = [nodeExternals({ modulesDir })];
|
|
||||||
} else if (Array.isArray(options.externalDependencies)) {
|
|
||||||
webpackConfig.externals = [
|
|
||||||
function (context, callback: Function) {
|
|
||||||
if (options.externalDependencies.includes(context.request)) {
|
|
||||||
// not bundled
|
|
||||||
return callback(null, `commonjs ${context.request}`);
|
|
||||||
}
|
|
||||||
// bundled
|
|
||||||
callback();
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.generatePackageJson) {
|
|
||||||
webpackConfig.plugins ??= [];
|
|
||||||
webpackConfig.plugins.push(
|
|
||||||
new GeneratePackageJsonWebpackPlugin(context, projectGraph, options)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return webpackConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getNodeWebpackConfig(
|
|
||||||
context: ExecutorContext,
|
|
||||||
projectGraph: ProjectGraph,
|
|
||||||
options: BuildNodeBuilderOptions
|
|
||||||
) {
|
|
||||||
return merge([
|
|
||||||
getBaseWebpackPartial(options),
|
|
||||||
getNodePartial(context, projectGraph, options),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@ -1,167 +0,0 @@
|
|||||||
import { normalizeBuildOptions } from './normalize';
|
|
||||||
import { BuildNodeBuilderOptions } from './types';
|
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
|
|
||||||
describe('normalizeBuildOptions', () => {
|
|
||||||
let testOptions: BuildNodeBuilderOptions;
|
|
||||||
let root: string;
|
|
||||||
let sourceRoot: string;
|
|
||||||
let projectRoot: string;
|
|
||||||
|
|
||||||
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,
|
|
||||||
externalDependencies: 'all',
|
|
||||||
};
|
|
||||||
root = '/root';
|
|
||||||
sourceRoot = 'apps/nodeapp/src';
|
|
||||||
projectRoot = 'apps/nodeapp';
|
|
||||||
});
|
|
||||||
it('should add the root', () => {
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
testOptions,
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
expect(result.root).toEqual('/root');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve main from root', () => {
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
testOptions,
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
expect(result.main).toEqual('/root/apps/nodeapp/src/main.ts');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve additional entries from root', () => {
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
{
|
|
||||||
...testOptions,
|
|
||||||
additionalEntryPoints: [
|
|
||||||
{ entryName: 'test', entryPath: 'some/path.ts' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
expect(result.additionalEntryPoints[0].entryPath).toEqual(
|
|
||||||
'/root/some/path.ts'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve the output path', () => {
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
testOptions,
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
expect(result.outputPath).toEqual('/root/dist/apps/nodeapp');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve the tsConfig path', () => {
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
testOptions,
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
expect(result.tsConfig).toEqual('/root/apps/nodeapp/tsconfig.app.json');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should normalize asset patterns', () => {
|
|
||||||
jest.spyOn(fs, 'statSync').mockReturnValue({
|
|
||||||
isDirectory: () => true,
|
|
||||||
} as any);
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
{
|
|
||||||
...testOptions,
|
|
||||||
root,
|
|
||||||
assets: [
|
|
||||||
'apps/nodeapp/src/assets',
|
|
||||||
{
|
|
||||||
input: 'outsideproj',
|
|
||||||
output: 'output',
|
|
||||||
glob: '**/*',
|
|
||||||
ignore: ['**/*.json'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
expect(result.assets).toEqual([
|
|
||||||
{
|
|
||||||
input: '/root/apps/nodeapp/src/assets',
|
|
||||||
output: 'assets',
|
|
||||||
glob: '**/*',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '/root/outsideproj',
|
|
||||||
output: 'output',
|
|
||||||
glob: '**/*',
|
|
||||||
ignore: ['**/*.json'],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve the file replacement paths', () => {
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
testOptions,
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve outputFileName correctly', () => {
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
testOptions,
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
expect(result.outputFileName).toEqual('main.js');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve outputFileName to "main.js" if not passed in', () => {
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
{ ...testOptions, outputFileName: 'index.js' },
|
|
||||||
root,
|
|
||||||
sourceRoot,
|
|
||||||
projectRoot
|
|
||||||
);
|
|
||||||
expect(result.outputFileName).toEqual('index.js');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
import type {
|
|
||||||
CustomTransformerFactory,
|
|
||||||
Node,
|
|
||||||
Program,
|
|
||||||
TransformerFactory as TypescriptTransformerFactory,
|
|
||||||
} from 'typescript';
|
|
||||||
|
|
||||||
export interface FileReplacement {
|
|
||||||
replace: string;
|
|
||||||
with: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OptimizationOptions {
|
|
||||||
scripts: boolean;
|
|
||||||
styles: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SourceMapOptions {
|
|
||||||
scripts: boolean;
|
|
||||||
styles: boolean;
|
|
||||||
vendors: boolean;
|
|
||||||
hidden: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
type TransformerFactory =
|
|
||||||
| TypescriptTransformerFactory<Node>
|
|
||||||
| CustomTransformerFactory;
|
|
||||||
|
|
||||||
export interface TransformerPlugin {
|
|
||||||
name: string;
|
|
||||||
options: Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TransformerEntry = string | TransformerPlugin;
|
|
||||||
|
|
||||||
export interface CompilerPlugin {
|
|
||||||
before?: (
|
|
||||||
options?: Record<string, unknown>,
|
|
||||||
program?: Program
|
|
||||||
) => TransformerFactory;
|
|
||||||
after?: (
|
|
||||||
options?: Record<string, unknown>,
|
|
||||||
program?: Program
|
|
||||||
) => TransformerFactory;
|
|
||||||
afterDeclarations?: (
|
|
||||||
options?: Record<string, unknown>,
|
|
||||||
program?: Program
|
|
||||||
) => TransformerFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CompilerPluginHooks {
|
|
||||||
beforeHooks: Array<(program?: Program) => TransformerFactory>;
|
|
||||||
afterHooks: Array<(program?: Program) => TransformerFactory>;
|
|
||||||
afterDeclarationsHooks: Array<(program?: Program) => TransformerFactory>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AdditionalEntryPoint {
|
|
||||||
entryName: string;
|
|
||||||
entryPath: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WebpackWatchOptions {
|
|
||||||
aggregateTimeout?: number;
|
|
||||||
ignored?: Array<string> | string;
|
|
||||||
poll?: number;
|
|
||||||
followSymlinks?: boolean;
|
|
||||||
stdin?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuildBuilderOptions {
|
|
||||||
main: string;
|
|
||||||
outputPath: string;
|
|
||||||
tsConfig: string;
|
|
||||||
watch?: boolean;
|
|
||||||
watchOptions?: WebpackWatchOptions;
|
|
||||||
sourceMap?: boolean | SourceMapOptions;
|
|
||||||
optimization?: boolean | OptimizationOptions;
|
|
||||||
maxWorkers?: number;
|
|
||||||
memoryLimit?: number;
|
|
||||||
poll?: number;
|
|
||||||
|
|
||||||
fileReplacements: FileReplacement[];
|
|
||||||
assets?: any[];
|
|
||||||
|
|
||||||
progress?: boolean;
|
|
||||||
statsJson?: boolean;
|
|
||||||
extractLicenses?: boolean;
|
|
||||||
verbose?: boolean;
|
|
||||||
|
|
||||||
webpackConfig?: string | string[];
|
|
||||||
|
|
||||||
root?: string;
|
|
||||||
sourceRoot?: string;
|
|
||||||
projectRoot?: string;
|
|
||||||
|
|
||||||
transformers?: TransformerEntry[];
|
|
||||||
|
|
||||||
additionalEntryPoints?: AdditionalEntryPoint[];
|
|
||||||
outputFileName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuildNodeBuilderOptions extends BuildBuilderOptions {
|
|
||||||
optimization?: boolean;
|
|
||||||
sourceMap?: boolean;
|
|
||||||
externalDependencies: 'all' | 'none' | string[];
|
|
||||||
buildLibsFromSource?: boolean;
|
|
||||||
generatePackageJson?: boolean;
|
|
||||||
deleteOutputPath?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NormalizedBuildNodeBuilderOptions
|
|
||||||
extends BuildNodeBuilderOptions {
|
|
||||||
webpackConfig: string[];
|
|
||||||
}
|
|
||||||
@ -39,6 +39,7 @@
|
|||||||
"@nrwl/linter": "file:../linter",
|
"@nrwl/linter": "file:../linter",
|
||||||
"@nrwl/storybook": "file:../storybook",
|
"@nrwl/storybook": "file:../storybook",
|
||||||
"@nrwl/web": "file:../web",
|
"@nrwl/web": "file:../web",
|
||||||
|
"@nrwl/webpack": "file:../webpack",
|
||||||
"@nrwl/workspace": "file:../workspace",
|
"@nrwl/workspace": "file:../workspace",
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
|
||||||
"@svgr/webpack": "^6.1.2",
|
"@svgr/webpack": "^6.1.2",
|
||||||
|
|||||||
@ -16,9 +16,9 @@ import {
|
|||||||
Target,
|
Target,
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import type { WebWebpackExecutorOptions } from '@nrwl/web/src/executors/webpack/webpack.impl';
|
import type { WebpackExecutorOptions } from '@nrwl/webpack/src/executors/webpack/schema';
|
||||||
import { normalizeWebBuildOptions } from '@nrwl/web/src/utils/normalize';
|
import { normalizeOptions } from '@nrwl/webpack/src/executors/webpack/lib/normalize-options';
|
||||||
import { getWebConfig } from '@nrwl/web/src/utils/web.config';
|
import { getWebpackConfig } from '@nrwl/webpack/src/executors/webpack/lib/get-webpack-config';
|
||||||
import { buildBaseWebpackConfig } from './webpack-fallback';
|
import { buildBaseWebpackConfig } from './webpack-fallback';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,8 +108,8 @@ export function nxComponentTestingPreset(
|
|||||||
function withSchemaDefaults(
|
function withSchemaDefaults(
|
||||||
target: Target,
|
target: Target,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
): WebWebpackExecutorOptions {
|
): WebpackExecutorOptions {
|
||||||
const options = readTargetOptions<WebWebpackExecutorOptions>(target, context);
|
const options = readTargetOptions<WebpackExecutorOptions>(target, context);
|
||||||
|
|
||||||
options.compiler ??= 'babel';
|
options.compiler ??= 'babel';
|
||||||
options.deleteOutputPath ??= true;
|
options.deleteOutputPath ??= true;
|
||||||
@ -149,18 +149,16 @@ function buildTargetWebpack(
|
|||||||
Has component config? ${!!ctProjectConfig}
|
Has component config? ${!!ctProjectConfig}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
const context = createExecutorContext(
|
||||||
|
graph,
|
||||||
|
buildableProjectConfig.targets,
|
||||||
|
parsed.project,
|
||||||
|
parsed.target,
|
||||||
|
parsed.target
|
||||||
|
);
|
||||||
|
|
||||||
const options = normalizeWebBuildOptions(
|
const options = normalizeOptions(
|
||||||
withSchemaDefaults(
|
withSchemaDefaults(parsed, context),
|
||||||
parsed,
|
|
||||||
createExecutorContext(
|
|
||||||
graph,
|
|
||||||
buildableProjectConfig.targets,
|
|
||||||
parsed.project,
|
|
||||||
parsed.target,
|
|
||||||
parsed.target
|
|
||||||
)
|
|
||||||
),
|
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
buildableProjectConfig.sourceRoot!
|
buildableProjectConfig.sourceRoot!
|
||||||
);
|
);
|
||||||
@ -171,13 +169,10 @@ function buildTargetWebpack(
|
|||||||
: options.optimization && options.optimization.scripts
|
: options.optimization && options.optimization.scripts
|
||||||
? options.optimization.scripts
|
? options.optimization.scripts
|
||||||
: false;
|
: false;
|
||||||
return getWebConfig(
|
|
||||||
workspaceRoot,
|
return getWebpackConfig(context, options, true, isScriptOptimizeOn, {
|
||||||
ctProjectConfig.root,
|
root: ctProjectConfig.root,
|
||||||
ctProjectConfig.sourceRoot,
|
sourceRoot: ctProjectConfig.sourceRoot,
|
||||||
options,
|
configuration: parsed.configuration,
|
||||||
true,
|
});
|
||||||
isScriptOptimizeOn,
|
|
||||||
parsed.configuration
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { getCSSModuleLocalIdent } from '@nrwl/web/src/utils/web.config';
|
import { getCSSModuleLocalIdent } from '@nrwl/webpack/src/executors/webpack/lib/get-webpack-config';
|
||||||
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
||||||
import { Configuration } from 'webpack';
|
import { Configuration } from 'webpack';
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { webpack } from './index';
|
import { webpack } from './index';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
jest.mock('@nrwl/web/src/utils/web.config', () => {
|
jest.mock('@nrwl/webpack/src/executors/webpack/lib/get-webpack-config', () => {
|
||||||
return {
|
return {
|
||||||
getStylesPartial: () => ({}),
|
getStylesPartial: () => ({}),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,17 +1,22 @@
|
|||||||
import {
|
import {
|
||||||
|
ExecutorContext,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
readJsonFile,
|
|
||||||
logger,
|
logger,
|
||||||
|
ProjectGraph,
|
||||||
|
readJsonFile,
|
||||||
|
readNxJson,
|
||||||
|
TargetConfiguration,
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { getBaseWebpackPartial } from '@nrwl/web/src/utils/config';
|
import { getBaseWebpackPartial } from '@nrwl/webpack/src/utils/config';
|
||||||
import { getStylesPartial } from '@nrwl/web/src/utils/web.config';
|
import { getStylesPartial } from '@nrwl/webpack/src/executors/webpack/lib/get-webpack-config';
|
||||||
import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils';
|
import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { gte } from 'semver';
|
import { gte } from 'semver';
|
||||||
import { Configuration, WebpackPluginInstance, DefinePlugin } from 'webpack';
|
import { Configuration, DefinePlugin, WebpackPluginInstance } from 'webpack';
|
||||||
import * as mergeWebpack from 'webpack-merge';
|
import * as mergeWebpack from 'webpack-merge';
|
||||||
import { mergePlugins } from './merge-plugins';
|
import { mergePlugins } from './merge-plugins';
|
||||||
|
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
|
||||||
|
|
||||||
const reactWebpackConfig = require('../webpack');
|
const reactWebpackConfig = require('../webpack');
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { ExecutorContext, logger, runExecutor } from '@nrwl/devkit';
|
import { ExecutorContext, logger, runExecutor } from '@nrwl/devkit';
|
||||||
import devServerExecutor, {
|
import devServerExecutor from '@nrwl/webpack/src/executors/dev-server/dev-server.impl';
|
||||||
WebDevServerOptions,
|
import { WebDevServerOptions } from '@nrwl/webpack/src/executors/dev-server/schema';
|
||||||
} from '@nrwl/web/src/executors/dev-server/dev-server.impl';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import {
|
import {
|
||||||
combineAsyncIterators,
|
combineAsyncIterators,
|
||||||
|
|||||||
@ -324,7 +324,7 @@ describe('app', () => {
|
|||||||
|
|
||||||
const workspaceJson = getProjects(appTree);
|
const workspaceJson = getProjects(appTree);
|
||||||
const targetConfig = workspaceJson.get('my-app').targets;
|
const targetConfig = workspaceJson.get('my-app').targets;
|
||||||
expect(targetConfig.build.executor).toEqual('@nrwl/web:webpack');
|
expect(targetConfig.build.executor).toEqual('@nrwl/webpack:webpack');
|
||||||
expect(targetConfig.build.outputs).toEqual(['{options.outputPath}']);
|
expect(targetConfig.build.outputs).toEqual(['{options.outputPath}']);
|
||||||
expect(targetConfig.build.options).toEqual({
|
expect(targetConfig.build.options).toEqual({
|
||||||
compiler: 'babel',
|
compiler: 'babel',
|
||||||
@ -360,7 +360,7 @@ describe('app', () => {
|
|||||||
|
|
||||||
const workspaceJson = getProjects(appTree);
|
const workspaceJson = getProjects(appTree);
|
||||||
const targetConfig = workspaceJson.get('my-app').targets;
|
const targetConfig = workspaceJson.get('my-app').targets;
|
||||||
expect(targetConfig.serve.executor).toEqual('@nrwl/web:dev-server');
|
expect(targetConfig.serve.executor).toEqual('@nrwl/webpack:dev-server');
|
||||||
expect(targetConfig.serve.options).toEqual({
|
expect(targetConfig.serve.options).toEqual({
|
||||||
buildTarget: 'my-app:build',
|
buildTarget: 'my-app:build',
|
||||||
hmr: true,
|
hmr: true,
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-ser
|
|||||||
import reactInitGenerator from '../init/init';
|
import reactInitGenerator from '../init/init';
|
||||||
import { lintProjectGenerator } from '@nrwl/linter';
|
import { lintProjectGenerator } from '@nrwl/linter';
|
||||||
import { swcCoreVersion } from '@nrwl/js/src/utils/versions';
|
import { swcCoreVersion } from '@nrwl/js/src/utils/versions';
|
||||||
import { swcLoaderVersion } from '@nrwl/web/src/utils/versions';
|
import { swcLoaderVersion } from '@nrwl/webpack/src/utils/versions';
|
||||||
|
|
||||||
async function addLinting(host: Tree, options: NormalizedSchema) {
|
async function addLinting(host: Tree, options: NormalizedSchema) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
|||||||
@ -36,7 +36,7 @@ function maybeJs(options: NormalizedSchema, path: string): string {
|
|||||||
|
|
||||||
function createBuildTarget(options: NormalizedSchema): TargetConfiguration {
|
function createBuildTarget(options: NormalizedSchema): TargetConfiguration {
|
||||||
return {
|
return {
|
||||||
executor: '@nrwl/web:webpack',
|
executor: '@nrwl/webpack:webpack',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
defaultConfiguration: 'production',
|
defaultConfiguration: 'production',
|
||||||
options: {
|
options: {
|
||||||
@ -102,7 +102,7 @@ function createBuildTarget(options: NormalizedSchema): TargetConfiguration {
|
|||||||
|
|
||||||
function createServeTarget(options: NormalizedSchema): TargetConfiguration {
|
function createServeTarget(options: NormalizedSchema): TargetConfiguration {
|
||||||
return {
|
return {
|
||||||
executor: '@nrwl/web:dev-server',
|
executor: '@nrwl/webpack:dev-server',
|
||||||
defaultConfiguration: 'development',
|
defaultConfiguration: 'development',
|
||||||
options: {
|
options: {
|
||||||
buildTarget: `${options.projectName}:build`,
|
buildTarget: `${options.projectName}:build`,
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export async function updateProjectConfig(
|
|||||||
const found = await findBuildConfig(tree, {
|
const found = await findBuildConfig(tree, {
|
||||||
project: options.project,
|
project: options.project,
|
||||||
buildTarget: options.buildTarget,
|
buildTarget: options.buildTarget,
|
||||||
validExecutorNames: new Set<string>(['@nrwl/web:webpack']),
|
validExecutorNames: new Set<string>(['@nrwl/webpack:webpack']),
|
||||||
});
|
});
|
||||||
|
|
||||||
assetValidConfig(found.config);
|
assetValidConfig(found.config);
|
||||||
|
|||||||
@ -8,7 +8,7 @@ export function updateProject(
|
|||||||
config: ProjectConfiguration,
|
config: ProjectConfiguration,
|
||||||
options: SetupTailwindOptions
|
options: SetupTailwindOptions
|
||||||
) {
|
) {
|
||||||
if (config?.targets?.build?.executor === '@nrwl/web:webpack') {
|
if (config?.targets?.build?.executor === '@nrwl/webpack:webpack') {
|
||||||
config.targets.build.options ??= {};
|
config.targets.build.options ??= {};
|
||||||
config.targets.build.options.postcssConfig = joinPathFragments(
|
config.targets.build.options.postcssConfig = joinPathFragments(
|
||||||
config.root,
|
config.root,
|
||||||
|
|||||||
@ -49,7 +49,7 @@ describe('setup-tailwind', () => {
|
|||||||
sourceRoot: 'apps/example/src',
|
sourceRoot: 'apps/example/src',
|
||||||
targets: {
|
targets: {
|
||||||
build: {
|
build: {
|
||||||
executor: '@nrwl/web:webpack',
|
executor: '@nrwl/webpack:webpack',
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
Tree,
|
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
|
Tree,
|
||||||
updateProjectConfiguration,
|
updateProjectConfiguration,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { WebRollupOptions } from '@nrwl/web/src/executors/rollup/schema';
|
|
||||||
import { forEachExecutorOptions } from '@nrwl/workspace/src/utilities/executor-options-utils';
|
import { forEachExecutorOptions } from '@nrwl/workspace/src/utilities/executor-options-utils';
|
||||||
|
|
||||||
export async function updateExternalEmotionJsxRuntime(tree: Tree) {
|
export async function updateExternalEmotionJsxRuntime(tree: Tree) {
|
||||||
forEachExecutorOptions<WebRollupOptions>(
|
forEachExecutorOptions<any>(
|
||||||
tree,
|
tree,
|
||||||
'@nrwl/web:rollup',
|
'@nrwl/web:rollup',
|
||||||
(options: any, projectName, targetName, configurationName) => {
|
(options: any, projectName, targetName, configurationName) => {
|
||||||
|
|||||||
@ -62,6 +62,12 @@
|
|||||||
"version": "13.8.0-beta.1",
|
"version": "13.8.0-beta.1",
|
||||||
"description": "Add a postcss config option to apps to load a single config file for all libs",
|
"description": "Add a postcss config option to apps to load a single config file for all libs",
|
||||||
"factory": "./src/migrations/update-13-8-0/add-postcss-config-option"
|
"factory": "./src/migrations/update-13-8-0/add-postcss-config-option"
|
||||||
|
},
|
||||||
|
"update-webpack-executor": {
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "14.7.5-beta.1",
|
||||||
|
"description": "Update usages of webpack executors to @nrwl/webpack",
|
||||||
|
"factory": "./src/migrations/update-14-7-5/update-webpack-executor"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageJsonUpdates": {
|
"packageJsonUpdates": {
|
||||||
|
|||||||
@ -42,46 +42,28 @@
|
|||||||
"@nrwl/jest": "file:../jest",
|
"@nrwl/jest": "file:../jest",
|
||||||
"@nrwl/js": "file:../js",
|
"@nrwl/js": "file:../js",
|
||||||
"@nrwl/linter": "file:../linter",
|
"@nrwl/linter": "file:../linter",
|
||||||
|
"@nrwl/webpack": "file:../webpack",
|
||||||
"@nrwl/workspace": "file:../workspace",
|
"@nrwl/workspace": "file:../workspace",
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
|
|
||||||
"@rollup/plugin-babel": "^5.3.0",
|
"@rollup/plugin-babel": "^5.3.0",
|
||||||
"@rollup/plugin-commonjs": "^20.0.0",
|
"@rollup/plugin-commonjs": "^20.0.0",
|
||||||
"@rollup/plugin-image": "^2.1.0",
|
"@rollup/plugin-image": "^2.1.0",
|
||||||
"@rollup/plugin-json": "^4.1.0",
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.9",
|
||||||
"babel-loader": "^8.2.2",
|
|
||||||
"babel-plugin-const-enum": "^1.0.1",
|
"babel-plugin-const-enum": "^1.0.1",
|
||||||
"babel-plugin-macros": "^2.8.0",
|
"babel-plugin-macros": "^2.8.0",
|
||||||
"babel-plugin-transform-async-to-promises": "^0.8.15",
|
"babel-plugin-transform-async-to-promises": "^0.8.15",
|
||||||
"babel-plugin-transform-typescript-metadata": "^0.3.1",
|
"babel-plugin-transform-typescript-metadata": "^0.3.1",
|
||||||
"browserslist": "^4.16.6",
|
|
||||||
"bytes": "^3.1.0",
|
"bytes": "^3.1.0",
|
||||||
"caniuse-lite": "^1.0.30001251",
|
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
"chokidar": "^3.5.1",
|
"chokidar": "^3.5.1",
|
||||||
"copy-webpack-plugin": "^10.2.4",
|
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"css-loader": "^6.4.0",
|
|
||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
|
||||||
"enhanced-resolve": "^5.8.3",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"fork-ts-checker-webpack-plugin": "7.2.13",
|
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"http-server": "14.1.0",
|
"http-server": "14.1.0",
|
||||||
"identity-obj-proxy": "3.0.0",
|
|
||||||
"ignore": "^5.0.4",
|
"ignore": "^5.0.4",
|
||||||
"less": "3.12.2",
|
"less": "3.12.2",
|
||||||
"less-loader": "^10.1.0",
|
|
||||||
"license-webpack-plugin": "^4.0.2",
|
|
||||||
"loader-utils": "1.2.3",
|
|
||||||
"mini-css-extract-plugin": "~2.4.7",
|
|
||||||
"parse5": "4.0.0",
|
|
||||||
"parse5-html-rewriting-stream": "6.0.1",
|
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
"postcss-import": "~14.1.0",
|
"postcss-import": "~14.1.0",
|
||||||
"postcss-loader": "^6.1.1",
|
|
||||||
"raw-loader": "^4.0.2",
|
|
||||||
"react-refresh": "^0.10.0",
|
"react-refresh": "^0.10.0",
|
||||||
"rollup": "^2.56.2",
|
"rollup": "^2.56.2",
|
||||||
"rollup-plugin-copy": "^3.4.0",
|
"rollup-plugin-copy": "^3.4.0",
|
||||||
@ -90,23 +72,11 @@
|
|||||||
"rollup-plugin-typescript2": "^0.31.1",
|
"rollup-plugin-typescript2": "^0.31.1",
|
||||||
"rxjs": "^6.5.4",
|
"rxjs": "^6.5.4",
|
||||||
"sass": "^1.42.1",
|
"sass": "^1.42.1",
|
||||||
"sass-loader": "^12.2.0",
|
|
||||||
"semver": "7.3.4",
|
"semver": "7.3.4",
|
||||||
"source-map": "0.7.3",
|
"source-map": "0.7.3",
|
||||||
"source-map-loader": "^3.0.0",
|
|
||||||
"style-loader": "^3.3.0",
|
|
||||||
"stylus": "^0.55.0",
|
"stylus": "^0.55.0",
|
||||||
"stylus-loader": "^6.2.0",
|
|
||||||
"terser-webpack-plugin": "^5.3.3",
|
|
||||||
"ts-loader": "^9.3.1",
|
|
||||||
"ts-node": "10.9.1",
|
"ts-node": "10.9.1",
|
||||||
"tsconfig-paths": "^3.9.0",
|
"tsconfig-paths": "^3.9.0",
|
||||||
"tsconfig-paths-webpack-plugin": "3.5.2",
|
"tslib": "^2.3.0"
|
||||||
"tslib": "^2.3.0",
|
|
||||||
"webpack": "^5.58.1",
|
|
||||||
"webpack-dev-server": "^4.9.3",
|
|
||||||
"webpack-merge": "^5.8.0",
|
|
||||||
"webpack-sources": "^3.2.3",
|
|
||||||
"webpack-subresource-integrity": "^5.1.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,134 +1,8 @@
|
|||||||
import * as webpack from 'webpack';
|
/**
|
||||||
import {
|
* This is here for backwards-compat.
|
||||||
ExecutorContext,
|
* TODO(jack): remove in Nx 16.
|
||||||
parseTargetString,
|
*/
|
||||||
readTargetOptions,
|
import { devServerExecutor, WebDevServerOptions } from '@nrwl/webpack';
|
||||||
} from '@nrwl/devkit';
|
|
||||||
|
|
||||||
import { eachValueFrom } from '@nrwl/devkit/src/utils/rxjs-for-await';
|
export { devServerExecutor, WebDevServerOptions };
|
||||||
import { map, tap } from 'rxjs/operators';
|
export default devServerExecutor;
|
||||||
import * as WebpackDevServer from 'webpack-dev-server';
|
|
||||||
|
|
||||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
|
||||||
import { WebWebpackExecutorOptions } from '../webpack/webpack.impl';
|
|
||||||
import { getDevServerConfig } from '../../utils/devserver.config';
|
|
||||||
import {
|
|
||||||
calculateProjectDependencies,
|
|
||||||
createTmpTsConfig,
|
|
||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
|
||||||
import { getEmittedFiles, runWebpackDevServer } from '../../utils/run-webpack';
|
|
||||||
import { resolveCustomWebpackConfig } from '../../utils/webpack/custom-webpack';
|
|
||||||
|
|
||||||
export interface WebDevServerOptions {
|
|
||||||
host: string;
|
|
||||||
port: number;
|
|
||||||
publicHost?: string;
|
|
||||||
ssl: boolean;
|
|
||||||
sslKey?: string;
|
|
||||||
sslCert?: string;
|
|
||||||
proxyConfig?: string;
|
|
||||||
buildTarget: string;
|
|
||||||
open: boolean;
|
|
||||||
liveReload: boolean;
|
|
||||||
hmr: boolean;
|
|
||||||
watch: boolean;
|
|
||||||
allowedHosts: string;
|
|
||||||
maxWorkers?: number;
|
|
||||||
memoryLimit?: number;
|
|
||||||
baseHref?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function* devServerExecutor(
|
|
||||||
serveOptions: WebDevServerOptions,
|
|
||||||
context: ExecutorContext
|
|
||||||
) {
|
|
||||||
const { root: projectRoot, sourceRoot } =
|
|
||||||
context.workspace.projects[context.projectName];
|
|
||||||
const buildOptions = normalizeWebBuildOptions(
|
|
||||||
getBuildOptions(serveOptions, context),
|
|
||||||
context.root,
|
|
||||||
sourceRoot
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!buildOptions.buildLibsFromSource) {
|
|
||||||
const { target, dependencies } = calculateProjectDependencies(
|
|
||||||
context.projectGraph,
|
|
||||||
context.root,
|
|
||||||
context.projectName,
|
|
||||||
'build', // should be generalized
|
|
||||||
context.configurationName
|
|
||||||
);
|
|
||||||
buildOptions.tsConfig = createTmpTsConfig(
|
|
||||||
buildOptions.tsConfig,
|
|
||||||
context.root,
|
|
||||||
target.data.root,
|
|
||||||
dependencies
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let webpackConfig = getDevServerConfig(
|
|
||||||
context.root,
|
|
||||||
projectRoot,
|
|
||||||
sourceRoot,
|
|
||||||
buildOptions,
|
|
||||||
serveOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
if (buildOptions.webpackConfig) {
|
|
||||||
let customWebpack = resolveCustomWebpackConfig(
|
|
||||||
buildOptions.webpackConfig,
|
|
||||||
buildOptions.tsConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
if (typeof customWebpack.then === 'function') {
|
|
||||||
customWebpack = await customWebpack;
|
|
||||||
}
|
|
||||||
|
|
||||||
webpackConfig = await customWebpack(webpackConfig, {
|
|
||||||
buildOptions,
|
|
||||||
configuration: serveOptions.buildTarget.split(':')[2],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return yield* eachValueFrom(
|
|
||||||
runWebpackDevServer(webpackConfig, webpack, WebpackDevServer).pipe(
|
|
||||||
tap(({ stats }) => {
|
|
||||||
console.info(stats.toString((webpackConfig as any).stats));
|
|
||||||
}),
|
|
||||||
map(({ baseUrl, stats }) => {
|
|
||||||
return {
|
|
||||||
baseUrl,
|
|
||||||
emittedFiles: getEmittedFiles(stats),
|
|
||||||
success: !stats.hasErrors(),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBuildOptions(
|
|
||||||
options: WebDevServerOptions,
|
|
||||||
context: ExecutorContext
|
|
||||||
): WebWebpackExecutorOptions {
|
|
||||||
const target = parseTargetString(options.buildTarget);
|
|
||||||
|
|
||||||
const overrides: Partial<WebWebpackExecutorOptions> = {
|
|
||||||
watch: false,
|
|
||||||
};
|
|
||||||
if (options.maxWorkers) {
|
|
||||||
overrides.maxWorkers = options.maxWorkers;
|
|
||||||
}
|
|
||||||
if (options.memoryLimit) {
|
|
||||||
overrides.memoryLimit = options.memoryLimit;
|
|
||||||
}
|
|
||||||
if (options.baseHref) {
|
|
||||||
overrides.baseHref = options.baseHref;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildOptions = readTargetOptions(target, context);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...buildOptions,
|
|
||||||
...overrides,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
|
|
||||||
import { AssetGlobPattern } from '../../../utils/shared-models';
|
import { AssetGlobPattern } from '@nrwl/webpack';
|
||||||
import { normalizeAssets, normalizePluginPath } from '../../../utils/normalize';
|
import { normalizeAssets, normalizePluginPath } from '@nrwl/webpack';
|
||||||
import { WebRollupOptions } from '../schema';
|
import { WebRollupOptions } from '../schema';
|
||||||
|
|
||||||
export interface NormalizedWebRollupOptions extends WebRollupOptions {
|
export interface NormalizedWebRollupOptions extends WebRollupOptions {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import {
|
|||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
import resolve from '@rollup/plugin-node-resolve';
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
|
||||||
import { AssetGlobPattern } from '../../utils/shared-models';
|
import { AssetGlobPattern } from '@nrwl/webpack';
|
||||||
import { WebRollupOptions } from './schema';
|
import { WebRollupOptions } from './schema';
|
||||||
import { runRollup } from './lib/run-rollup';
|
import { runRollup } from './lib/run-rollup';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { convertNxExecutor } from '@nrwl/devkit';
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
import { run } from './webpack.impl';
|
import { webpackExecutor } from './webpack.impl';
|
||||||
|
|
||||||
export default convertNxExecutor(run);
|
export default convertNxExecutor(webpackExecutor);
|
||||||
|
|||||||
@ -1,249 +1,11 @@
|
|||||||
import { ExecutorContext, logger } from '@nrwl/devkit';
|
/**
|
||||||
import { eachValueFrom } from '@nrwl/devkit/src/utils/rxjs-for-await';
|
* This is here for backwards-compat.
|
||||||
import type { Configuration, Stats } from 'webpack';
|
* TODO(jack): remove in Nx 16.
|
||||||
import { from, of } from 'rxjs';
|
*/
|
||||||
import {
|
import {
|
||||||
bufferCount,
|
webpackExecutor,
|
||||||
mergeMap,
|
WebpackExecutorOptions as WebWebpackExecutorOptions,
|
||||||
mergeScan,
|
} from '@nrwl/webpack';
|
||||||
switchMap,
|
|
||||||
tap,
|
|
||||||
} from 'rxjs/operators';
|
|
||||||
import { execSync } from 'child_process';
|
|
||||||
import { Range, satisfies } from 'semver';
|
|
||||||
import { basename, join } from 'path';
|
|
||||||
import {
|
|
||||||
calculateProjectDependencies,
|
|
||||||
createTmpTsConfig,
|
|
||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
|
||||||
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
|
||||||
|
|
||||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
export { webpackExecutor, WebWebpackExecutorOptions };
|
||||||
import { getWebConfig } from '../../utils/web.config';
|
export default webpackExecutor;
|
||||||
import type { BuildBuilderOptions } from '../../utils/shared-models';
|
|
||||||
import { ExtraEntryPoint } from '../../utils/shared-models';
|
|
||||||
import { getEmittedFiles, runWebpack } from '../../utils/run-webpack';
|
|
||||||
import { BuildBrowserFeatures } from '../../utils/webpack/build-browser-features';
|
|
||||||
import { deleteOutputDir } from '../../utils/fs';
|
|
||||||
import {
|
|
||||||
CrossOriginValue,
|
|
||||||
writeIndexHtml,
|
|
||||||
} from '../../utils/webpack/write-index-html';
|
|
||||||
import { resolveCustomWebpackConfig } from '../../utils/webpack/custom-webpack';
|
|
||||||
|
|
||||||
export interface WebWebpackExecutorOptions extends BuildBuilderOptions {
|
|
||||||
index: string;
|
|
||||||
budgets?: any[];
|
|
||||||
baseHref?: string;
|
|
||||||
deployUrl?: string;
|
|
||||||
|
|
||||||
crossOrigin?: CrossOriginValue;
|
|
||||||
|
|
||||||
polyfills?: string;
|
|
||||||
es2015Polyfills?: string;
|
|
||||||
|
|
||||||
scripts: ExtraEntryPoint[];
|
|
||||||
styles: ExtraEntryPoint[];
|
|
||||||
|
|
||||||
vendorChunk?: boolean;
|
|
||||||
commonChunk?: boolean;
|
|
||||||
runtimeChunk?: boolean;
|
|
||||||
|
|
||||||
namedChunks?: boolean;
|
|
||||||
|
|
||||||
stylePreprocessorOptions?: any;
|
|
||||||
subresourceIntegrity?: boolean;
|
|
||||||
|
|
||||||
verbose?: boolean;
|
|
||||||
buildLibsFromSource?: boolean;
|
|
||||||
|
|
||||||
deleteOutputPath?: boolean;
|
|
||||||
|
|
||||||
generateIndexHtml?: boolean;
|
|
||||||
|
|
||||||
postcssConfig?: string;
|
|
||||||
|
|
||||||
extractCss?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getWebpackConfigs(
|
|
||||||
options: WebWebpackExecutorOptions,
|
|
||||||
context: ExecutorContext
|
|
||||||
): Promise<Configuration[]> {
|
|
||||||
const metadata = context.workspace.projects[context.projectName];
|
|
||||||
const sourceRoot = metadata.sourceRoot;
|
|
||||||
const projectRoot = metadata.root;
|
|
||||||
options = normalizeWebBuildOptions(options, context.root, sourceRoot);
|
|
||||||
const isScriptOptimizeOn =
|
|
||||||
typeof options.optimization === 'boolean'
|
|
||||||
? options.optimization
|
|
||||||
: options.optimization && options.optimization.scripts
|
|
||||||
? options.optimization.scripts
|
|
||||||
: false;
|
|
||||||
const tsConfig = readTsConfig(options.tsConfig);
|
|
||||||
const scriptTarget = tsConfig.options.target;
|
|
||||||
|
|
||||||
const buildBrowserFeatures = new BuildBrowserFeatures(
|
|
||||||
projectRoot,
|
|
||||||
scriptTarget
|
|
||||||
);
|
|
||||||
|
|
||||||
let customWebpack = null;
|
|
||||||
|
|
||||||
if (options.webpackConfig) {
|
|
||||||
customWebpack = resolveCustomWebpackConfig(
|
|
||||||
options.webpackConfig,
|
|
||||||
options.tsConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
if (typeof customWebpack.then === 'function') {
|
|
||||||
customWebpack = await customWebpack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
[
|
|
||||||
// ESM build for modern browsers.
|
|
||||||
getWebConfig(
|
|
||||||
context.root,
|
|
||||||
projectRoot,
|
|
||||||
sourceRoot,
|
|
||||||
options,
|
|
||||||
true,
|
|
||||||
isScriptOptimizeOn,
|
|
||||||
context.configurationName
|
|
||||||
),
|
|
||||||
// ES5 build for legacy browsers.
|
|
||||||
isScriptOptimizeOn && buildBrowserFeatures.isDifferentialLoadingNeeded()
|
|
||||||
? getWebConfig(
|
|
||||||
context.root,
|
|
||||||
projectRoot,
|
|
||||||
sourceRoot,
|
|
||||||
options,
|
|
||||||
false,
|
|
||||||
isScriptOptimizeOn,
|
|
||||||
context.configurationName
|
|
||||||
)
|
|
||||||
: undefined,
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.map(async (config) => {
|
|
||||||
if (customWebpack) {
|
|
||||||
return await customWebpack(config, {
|
|
||||||
options,
|
|
||||||
configuration: context.configurationName,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function* run(
|
|
||||||
options: WebWebpackExecutorOptions,
|
|
||||||
context: ExecutorContext
|
|
||||||
) {
|
|
||||||
// Node versions 12.2-12.8 has a bug where prod builds will hang for 2-3 minutes
|
|
||||||
// after the program exits.
|
|
||||||
const nodeVersion = execSync(`node --version`).toString('utf-8').trim();
|
|
||||||
const supportedRange = new Range('10 || >=12.9');
|
|
||||||
if (!satisfies(nodeVersion, supportedRange)) {
|
|
||||||
throw new Error(
|
|
||||||
`Node version ${nodeVersion} is not supported. Supported range is "${supportedRange.raw}".`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isScriptOptimizeOn =
|
|
||||||
typeof options.optimization === 'boolean'
|
|
||||||
? options.optimization
|
|
||||||
: options.optimization && options.optimization.scripts
|
|
||||||
? options.optimization.scripts
|
|
||||||
: false;
|
|
||||||
|
|
||||||
process.env.NODE_ENV ||= isScriptOptimizeOn ? 'production' : 'development';
|
|
||||||
|
|
||||||
const metadata = context.workspace.projects[context.projectName];
|
|
||||||
|
|
||||||
if (options.compiler === 'swc') {
|
|
||||||
try {
|
|
||||||
require.resolve('swc-loader');
|
|
||||||
require.resolve('@swc/core');
|
|
||||||
} catch {
|
|
||||||
logger.error(
|
|
||||||
`Missing SWC dependencies: @swc/core, swc-loader. Make sure you install them first.`
|
|
||||||
);
|
|
||||||
return { success: false };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.buildLibsFromSource && context.targetName) {
|
|
||||||
const { dependencies } = calculateProjectDependencies(
|
|
||||||
context.projectGraph,
|
|
||||||
context.root,
|
|
||||||
context.projectName,
|
|
||||||
context.targetName,
|
|
||||||
context.configurationName
|
|
||||||
);
|
|
||||||
options.tsConfig = createTmpTsConfig(
|
|
||||||
join(context.root, options.tsConfig),
|
|
||||||
context.root,
|
|
||||||
metadata.root,
|
|
||||||
dependencies
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete output path before bundling
|
|
||||||
if (options.deleteOutputPath) {
|
|
||||||
deleteOutputDir(context.root, options.outputPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
const configs = await getWebpackConfigs(options, context);
|
|
||||||
return yield* eachValueFrom(
|
|
||||||
from(configs).pipe(
|
|
||||||
mergeMap((config) => (Array.isArray(config) ? from(config) : of(config))),
|
|
||||||
// Run build sequentially and bail when first one fails.
|
|
||||||
mergeScan(
|
|
||||||
(acc, config) => {
|
|
||||||
if (!acc.hasErrors()) {
|
|
||||||
return runWebpack(config).pipe(
|
|
||||||
tap((stats) => {
|
|
||||||
console.info(stats.toString(config.stats));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return of();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ hasErrors: () => false } as Stats,
|
|
||||||
1
|
|
||||||
),
|
|
||||||
// Collect build results as an array.
|
|
||||||
bufferCount(configs.length),
|
|
||||||
switchMap(async ([result1, result2]) => {
|
|
||||||
const success =
|
|
||||||
result1 && !result1.hasErrors() && (!result2 || !result2.hasErrors());
|
|
||||||
const emittedFiles1 = getEmittedFiles(result1);
|
|
||||||
const emittedFiles2 = result2 ? getEmittedFiles(result2) : [];
|
|
||||||
if (options.generateIndexHtml) {
|
|
||||||
await writeIndexHtml({
|
|
||||||
crossOrigin: options.crossOrigin,
|
|
||||||
sri: options.subresourceIntegrity,
|
|
||||||
outputPath: join(options.outputPath, basename(options.index)),
|
|
||||||
indexPath: join(context.root, options.index),
|
|
||||||
files: emittedFiles1.filter((x) => x.extension === '.css'),
|
|
||||||
noModuleFiles: emittedFiles2,
|
|
||||||
moduleFiles: emittedFiles1,
|
|
||||||
baseHref: options.baseHref,
|
|
||||||
deployUrl: options.deployUrl,
|
|
||||||
scripts: options.scripts,
|
|
||||||
styles: options.styles,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return { success, emittedFiles: [...emittedFiles1, ...emittedFiles2] };
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default run;
|
|
||||||
|
|||||||
@ -299,7 +299,7 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
const workspaceJson = readJson(tree, 'workspace.json');
|
const workspaceJson = readJson(tree, 'workspace.json');
|
||||||
const architectConfig = workspaceJson.projects['my-app'].architect;
|
const architectConfig = workspaceJson.projects['my-app'].architect;
|
||||||
expect(architectConfig.build.builder).toEqual('@nrwl/web:webpack');
|
expect(architectConfig.build.builder).toEqual('@nrwl/webpack:webpack');
|
||||||
expect(architectConfig.build.outputs).toEqual(['{options.outputPath}']);
|
expect(architectConfig.build.outputs).toEqual(['{options.outputPath}']);
|
||||||
expect(architectConfig.build.options).toEqual({
|
expect(architectConfig.build.options).toEqual({
|
||||||
compiler: 'babel',
|
compiler: 'babel',
|
||||||
@ -336,7 +336,7 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
const workspaceJson = readJson(tree, 'workspace.json');
|
const workspaceJson = readJson(tree, 'workspace.json');
|
||||||
const architectConfig = workspaceJson.projects['my-app'].architect;
|
const architectConfig = workspaceJson.projects['my-app'].architect;
|
||||||
expect(architectConfig.serve.builder).toEqual('@nrwl/web:dev-server');
|
expect(architectConfig.serve.builder).toEqual('@nrwl/webpack:dev-server');
|
||||||
expect(architectConfig.serve.options).toEqual({
|
expect(architectConfig.serve.options).toEqual({
|
||||||
buildTarget: 'my-app:build',
|
buildTarget: 'my-app:build',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import { webpackProjectGenerator } from '@nrwl/webpack';
|
||||||
import { cypressProjectGenerator } from '@nrwl/cypress';
|
import { cypressProjectGenerator } from '@nrwl/cypress';
|
||||||
import {
|
import {
|
||||||
addDependenciesToPackageJson,
|
addDependenciesToPackageJson,
|
||||||
@ -10,10 +12,11 @@ import {
|
|||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
names,
|
names,
|
||||||
offsetFromRoot,
|
offsetFromRoot,
|
||||||
ProjectConfiguration,
|
readProjectConfiguration,
|
||||||
readWorkspaceConfiguration,
|
readWorkspaceConfiguration,
|
||||||
TargetConfiguration,
|
TargetConfiguration,
|
||||||
Tree,
|
Tree,
|
||||||
|
updateProjectConfiguration,
|
||||||
updateWorkspaceConfiguration,
|
updateWorkspaceConfiguration,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { jestProjectGenerator } from '@nrwl/jest';
|
import { jestProjectGenerator } from '@nrwl/jest';
|
||||||
@ -22,11 +25,7 @@ import { Linter, lintProjectGenerator } from '@nrwl/linter';
|
|||||||
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||||
import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
||||||
|
|
||||||
import { join } from 'path';
|
|
||||||
|
|
||||||
import { WebWebpackExecutorOptions } from '../../executors/webpack/webpack.impl';
|
|
||||||
import { swcLoaderVersion } from '../../utils/versions';
|
import { swcLoaderVersion } from '../../utils/versions';
|
||||||
|
|
||||||
import { webInitGenerator } from '../init/init';
|
import { webInitGenerator } from '../init/init';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
|
||||||
@ -54,29 +53,43 @@ function createApplicationFiles(tree: Tree, options: NormalizedSchema) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addBuildTarget(
|
async function setupBundler(tree: Tree, options: NormalizedSchema) {
|
||||||
project: ProjectConfiguration,
|
const main = joinPathFragments(options.appProjectRoot, 'src/main.ts');
|
||||||
options: NormalizedSchema
|
const tsConfig = joinPathFragments(
|
||||||
): ProjectConfiguration {
|
options.appProjectRoot,
|
||||||
const buildOptions: WebWebpackExecutorOptions = {
|
'tsconfig.app.json'
|
||||||
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
);
|
||||||
compiler: options.compiler ?? 'babel',
|
const assets = [
|
||||||
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
|
joinPathFragments(options.appProjectRoot, 'src/favicon.ico'),
|
||||||
baseHref: '/',
|
joinPathFragments(options.appProjectRoot, 'src/assets'),
|
||||||
main: joinPathFragments(options.appProjectRoot, 'src/main.ts'),
|
];
|
||||||
polyfills: joinPathFragments(options.appProjectRoot, 'src/polyfills.ts'),
|
|
||||||
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
if (options.bundler === 'webpack') {
|
||||||
assets: [
|
await webpackProjectGenerator(tree, {
|
||||||
joinPathFragments(options.appProjectRoot, 'src/favicon.ico'),
|
project: options.projectName,
|
||||||
joinPathFragments(options.appProjectRoot, 'src/assets'),
|
main,
|
||||||
],
|
tsConfig,
|
||||||
styles: [
|
compiler: options.compiler ?? 'babel',
|
||||||
|
devServer: true,
|
||||||
|
});
|
||||||
|
const project = readProjectConfiguration(tree, options.projectName);
|
||||||
|
const prodConfig = project.targets.build.configurations.production;
|
||||||
|
const buildOptions = project.targets.build.options;
|
||||||
|
buildOptions.assets = assets;
|
||||||
|
buildOptions.index = joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'src/index.html'
|
||||||
|
);
|
||||||
|
buildOptions.baseHref = '/';
|
||||||
|
buildOptions.polyfills = joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'src/polyfills.ts'
|
||||||
|
);
|
||||||
|
buildOptions.styles = [
|
||||||
joinPathFragments(options.appProjectRoot, `src/styles.${options.style}`),
|
joinPathFragments(options.appProjectRoot, `src/styles.${options.style}`),
|
||||||
],
|
];
|
||||||
scripts: [],
|
buildOptions.scripts = [];
|
||||||
};
|
prodConfig.fileReplacements = [
|
||||||
const productionBuildOptions: Partial<WebWebpackExecutorOptions> = {
|
|
||||||
fileReplacements: [
|
|
||||||
{
|
{
|
||||||
replace: joinPathFragments(
|
replace: joinPathFragments(
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
@ -87,77 +100,51 @@ function addBuildTarget(
|
|||||||
`src/environments/environment.prod.ts`
|
`src/environments/environment.prod.ts`
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
optimization: true,
|
prodConfig.optimization = true;
|
||||||
outputHashing: 'all',
|
prodConfig.outputHashing = 'all';
|
||||||
sourceMap: false,
|
prodConfig.sourceMap = false;
|
||||||
namedChunks: false,
|
prodConfig.namedChunks = false;
|
||||||
extractLicenses: true,
|
prodConfig.extractLicenses = true;
|
||||||
vendorChunk: false,
|
prodConfig.vendorChunk = false;
|
||||||
};
|
updateProjectConfiguration(tree, options.projectName, project);
|
||||||
|
} else if (options.bundler === 'none') {
|
||||||
return {
|
// TODO(jack): Flush this out... no bundler should be possible for web but the experience isn't holistic due to missing features (e.g. writing index.html).
|
||||||
...project,
|
const project = readProjectConfiguration(tree, options.projectName);
|
||||||
targets: {
|
project.targets.build = {
|
||||||
...project.targets,
|
executor: `@nrwl/js:${options.compiler}`,
|
||||||
build: {
|
outputs: ['{options.outputPath}'],
|
||||||
executor: '@nrwl/web:webpack',
|
options: {
|
||||||
outputs: ['{options.outputPath}'],
|
main,
|
||||||
defaultConfiguration: 'production',
|
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||||
options: buildOptions,
|
tsConfig,
|
||||||
configurations: {
|
assets,
|
||||||
production: productionBuildOptions,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
};
|
updateProjectConfiguration(tree, options.projectName, project);
|
||||||
|
} else {
|
||||||
|
throw new Error('Unsupported bundler type');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addServeTarget(
|
async function addProject(tree: Tree, options: NormalizedSchema) {
|
||||||
project: ProjectConfiguration,
|
|
||||||
options: NormalizedSchema
|
|
||||||
) {
|
|
||||||
const serveTarget: TargetConfiguration = {
|
|
||||||
executor: '@nrwl/web:dev-server',
|
|
||||||
options: {
|
|
||||||
buildTarget: `${options.projectName}:build`,
|
|
||||||
},
|
|
||||||
configurations: {
|
|
||||||
production: {
|
|
||||||
buildTarget: `${options.projectName}:build:production`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
...project,
|
|
||||||
targets: {
|
|
||||||
...project.targets,
|
|
||||||
serve: serveTarget,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function addProject(tree: Tree, options: NormalizedSchema) {
|
|
||||||
const targets: Record<string, TargetConfiguration> = {};
|
const targets: Record<string, TargetConfiguration> = {};
|
||||||
let project: ProjectConfiguration = {
|
|
||||||
projectType: 'application',
|
|
||||||
root: options.appProjectRoot,
|
|
||||||
sourceRoot: joinPathFragments(options.appProjectRoot, 'src'),
|
|
||||||
tags: options.parsedTags,
|
|
||||||
targets,
|
|
||||||
};
|
|
||||||
|
|
||||||
project = addBuildTarget(project, options);
|
|
||||||
project = addServeTarget(project, options);
|
|
||||||
|
|
||||||
addProjectConfiguration(
|
addProjectConfiguration(
|
||||||
tree,
|
tree,
|
||||||
options.projectName,
|
options.projectName,
|
||||||
project,
|
{
|
||||||
|
projectType: 'application',
|
||||||
|
root: options.appProjectRoot,
|
||||||
|
sourceRoot: joinPathFragments(options.appProjectRoot, 'src'),
|
||||||
|
tags: options.parsedTags,
|
||||||
|
targets,
|
||||||
|
},
|
||||||
options.standaloneConfig
|
options.standaloneConfig
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await setupBundler(tree, options);
|
||||||
|
|
||||||
const workspace = readWorkspaceConfiguration(tree);
|
const workspace = readWorkspaceConfiguration(tree);
|
||||||
|
|
||||||
if (!workspace.defaultProject) {
|
if (!workspace.defaultProject) {
|
||||||
@ -198,7 +185,7 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
|||||||
tasks.push(webTask);
|
tasks.push(webTask);
|
||||||
|
|
||||||
createApplicationFiles(host, options);
|
createApplicationFiles(host, options);
|
||||||
addProject(host, options);
|
await addProject(host, options);
|
||||||
|
|
||||||
const lintTask = await lintProjectGenerator(host, {
|
const lintTask = await lintProjectGenerator(host, {
|
||||||
linter: options.linter,
|
linter: options.linter,
|
||||||
@ -275,6 +262,8 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
|||||||
...options,
|
...options,
|
||||||
prefix: options.prefix ?? npmScope,
|
prefix: options.prefix ?? npmScope,
|
||||||
name: names(options.name).fileName,
|
name: names(options.name).fileName,
|
||||||
|
compiler: options.compiler ?? 'babel',
|
||||||
|
bundler: options.bundler ?? 'webpack',
|
||||||
projectName: appProjectName,
|
projectName: appProjectName,
|
||||||
appProjectRoot,
|
appProjectRoot,
|
||||||
e2eProjectRoot,
|
e2eProjectRoot,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export interface Schema {
|
|||||||
name: string;
|
name: string;
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
style?: string;
|
style?: string;
|
||||||
|
bundler?: 'webpack' | 'none';
|
||||||
compiler?: 'babel' | 'swc';
|
compiler?: 'babel' | 'swc';
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
|
|||||||
@ -50,6 +50,12 @@
|
|||||||
"enum": ["babel", "swc"],
|
"enum": ["babel", "swc"],
|
||||||
"default": "babel"
|
"default": "babel"
|
||||||
},
|
},
|
||||||
|
"bundler": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"enum": ["webpack", "none"],
|
||||||
|
"default": "webpack"
|
||||||
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -45,58 +45,4 @@ describe('init', () => {
|
|||||||
});
|
});
|
||||||
expect(tree.exists('jest.config.js')).toBe(false);
|
expect(tree.exists('jest.config.js')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('babel config', () => {
|
|
||||||
it('should create babel config if not present', async () => {
|
|
||||||
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
|
|
||||||
json.namedInputs = {
|
|
||||||
sharedGlobals: ['{workspaceRoot}/exiting-file.json'],
|
|
||||||
};
|
|
||||||
return json;
|
|
||||||
});
|
|
||||||
|
|
||||||
await webInitGenerator(tree, {
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('babel.config.json')).toBe(true);
|
|
||||||
const sharedGloabls = readJson<NxJsonConfiguration>(tree, 'nx.json')
|
|
||||||
.namedInputs.sharedGlobals;
|
|
||||||
expect(sharedGloabls).toContain('{workspaceRoot}/exiting-file.json');
|
|
||||||
expect(sharedGloabls).toContain('{workspaceRoot}/babel.config.json');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not overwrite existing babel config', async () => {
|
|
||||||
tree.write('babel.config.json', '{ "preset": ["preset-awesome"] }');
|
|
||||||
|
|
||||||
await webInitGenerator(tree, {
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
});
|
|
||||||
|
|
||||||
const existing = readJson(tree, 'babel.config.json');
|
|
||||||
expect(existing).toEqual({ preset: ['preset-awesome'] });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not overwrite existing babel config (.js)', async () => {
|
|
||||||
tree.write('/babel.config.js', 'module.exports = () => {};');
|
|
||||||
await webInitGenerator(tree, {
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
});
|
|
||||||
expect(tree.exists('babel.config.json')).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not fail when dependencies is missing from package.json and no other init generators are invoked', async () => {
|
|
||||||
updateJson(tree, 'package.json', (json) => {
|
|
||||||
delete json.dependencies;
|
|
||||||
return json;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(
|
|
||||||
webInitGenerator(tree, {
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
})
|
|
||||||
).resolves.toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -19,9 +19,18 @@ import {
|
|||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
|
||||||
function updateDependencies(tree: Tree) {
|
function updateDependencies(tree: Tree, schema: Schema) {
|
||||||
removeDependenciesFromPackageJson(tree, ['@nrwl/web'], []);
|
removeDependenciesFromPackageJson(tree, ['@nrwl/web'], []);
|
||||||
|
|
||||||
|
const devDependencies = {
|
||||||
|
'@nrwl/web': nxVersion,
|
||||||
|
'@types/node': typesNodeVersion,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (schema.bundler === 'webpack') {
|
||||||
|
devDependencies['@nrwl/webpack'] = nxVersion;
|
||||||
|
}
|
||||||
|
|
||||||
return addDependenciesToPackageJson(
|
return addDependenciesToPackageJson(
|
||||||
tree,
|
tree,
|
||||||
{
|
{
|
||||||
@ -29,10 +38,7 @@ function updateDependencies(tree: Tree) {
|
|||||||
'regenerator-runtime': '0.13.7',
|
'regenerator-runtime': '0.13.7',
|
||||||
tslib: tsLibVersion,
|
tslib: tsLibVersion,
|
||||||
},
|
},
|
||||||
{
|
devDependencies
|
||||||
'@nrwl/web': nxVersion,
|
|
||||||
'@types/node': typesNodeVersion,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +72,7 @@ export async function webInitGenerator(tree: Tree, schema: Schema) {
|
|||||||
const cypressTask = cypressInitGenerator(tree, {});
|
const cypressTask = cypressInitGenerator(tree, {});
|
||||||
tasks.push(cypressTask);
|
tasks.push(cypressTask);
|
||||||
}
|
}
|
||||||
const installTask = updateDependencies(tree);
|
const installTask = updateDependencies(tree, schema);
|
||||||
tasks.push(installTask);
|
tasks.push(installTask);
|
||||||
initRootBabelConfig(tree);
|
initRootBabelConfig(tree);
|
||||||
if (!schema.skipFormat) {
|
if (!schema.skipFormat) {
|
||||||
|
|||||||
1
packages/web/src/generators/init/schema.d.ts
vendored
1
packages/web/src/generators/init/schema.d.ts
vendored
@ -1,4 +1,5 @@
|
|||||||
export interface Schema {
|
export interface Schema {
|
||||||
|
bundler?: 'webpack' | 'none';
|
||||||
unitTestRunner?: 'jest' | 'none';
|
unitTestRunner?: 'jest' | 'none';
|
||||||
e2eTestRunner?: 'cypress' | 'none';
|
e2eTestRunner?: 'cypress' | 'none';
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
|
|||||||
@ -6,6 +6,12 @@
|
|||||||
"description": "Init Web Plugin.",
|
"description": "Init Web Plugin.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"bundler": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"enum": ["webpack", "none"],
|
||||||
|
"default": "webpack"
|
||||||
|
},
|
||||||
"unitTestRunner": {
|
"unitTestRunner": {
|
||||||
"description": "Adds the specified unit test runner",
|
"description": "Adds the specified unit test runner",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { getProjects, ProjectGraph, DependencyType } from '@nrwl/devkit';
|
import { getProjects, ProjectGraph, DependencyType } from '@nrwl/devkit';
|
||||||
import { reverse } from '@nrwl/devkit';
|
import { reverse } from '@nrwl/devkit';
|
||||||
import { hasDependentAppUsingWebBuild } from '@nrwl/web/src/migrations/update-11-5-2/utils';
|
import { hasDependentAppUsingWebBuild } from './utils';
|
||||||
|
|
||||||
describe('hasDependentAppUsingWebBuild', () => {
|
describe('hasDependentAppUsingWebBuild', () => {
|
||||||
const graph: ProjectGraph = reverse({
|
const graph: ProjectGraph = reverse({
|
||||||
|
|||||||
@ -0,0 +1,92 @@
|
|||||||
|
import { readJson } from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
|
||||||
|
|
||||||
|
import update from './update-webpack-executor';
|
||||||
|
|
||||||
|
describe('Migration: @nrwl/webpack', () => {
|
||||||
|
it(`should update usage of webpack executor`, async () => {
|
||||||
|
let tree = createTreeWithEmptyV1Workspace();
|
||||||
|
|
||||||
|
tree.write(
|
||||||
|
'workspace.json',
|
||||||
|
JSON.stringify({
|
||||||
|
version: 2,
|
||||||
|
projects: {
|
||||||
|
myapp: {
|
||||||
|
root: 'apps/myapp',
|
||||||
|
sourceRoot: 'apps/myapp/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nrwl/web:webpack',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await update(tree);
|
||||||
|
|
||||||
|
expect(readJson(tree, 'workspace.json')).toEqual({
|
||||||
|
version: 2,
|
||||||
|
projects: {
|
||||||
|
myapp: {
|
||||||
|
root: 'apps/myapp',
|
||||||
|
sourceRoot: 'apps/myapp/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nrwl/webpack:webpack',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should update usage of dev-server executor`, async () => {
|
||||||
|
let tree = createTreeWithEmptyV1Workspace();
|
||||||
|
|
||||||
|
tree.write(
|
||||||
|
'workspace.json',
|
||||||
|
JSON.stringify({
|
||||||
|
version: 2,
|
||||||
|
projects: {
|
||||||
|
myapp: {
|
||||||
|
root: 'apps/myapp',
|
||||||
|
sourceRoot: 'apps/myapp/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
serve: {
|
||||||
|
executor: '@nrwl/web:dev-server',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await update(tree);
|
||||||
|
|
||||||
|
expect(readJson(tree, 'workspace.json')).toEqual({
|
||||||
|
version: 2,
|
||||||
|
projects: {
|
||||||
|
myapp: {
|
||||||
|
root: 'apps/myapp',
|
||||||
|
sourceRoot: 'apps/myapp/src',
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
serve: {
|
||||||
|
executor: '@nrwl/webpack:dev-server',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
formatFiles,
|
||||||
|
getProjects,
|
||||||
|
Tree,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export default async function update(host: Tree) {
|
||||||
|
const projects = getProjects(host);
|
||||||
|
|
||||||
|
for (const [name, config] of projects.entries()) {
|
||||||
|
let updated = false;
|
||||||
|
if (config?.targets?.build?.executor === '@nrwl/web:webpack') {
|
||||||
|
config.targets.build.executor = '@nrwl/webpack:webpack';
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
if (config?.targets?.serve?.executor === '@nrwl/web:dev-server') {
|
||||||
|
config.targets.serve.executor = '@nrwl/webpack:dev-server';
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
if (updated) {
|
||||||
|
updateProjectConfiguration(host, name, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await formatFiles(host);
|
||||||
|
}
|
||||||
@ -1,305 +0,0 @@
|
|||||||
import { join } from 'path';
|
|
||||||
import * as webpack from 'webpack';
|
|
||||||
import { Configuration, WebpackPluginInstance } from 'webpack';
|
|
||||||
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
|
||||||
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
|
|
||||||
import { AssetGlobPattern, BuildBuilderOptions } from './shared-models';
|
|
||||||
import { getOutputHashFormat } from './hash-format';
|
|
||||||
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
|
||||||
import TerserPlugin = require('terser-webpack-plugin');
|
|
||||||
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
|
||||||
|
|
||||||
const IGNORED_WEBPACK_WARNINGS = [
|
|
||||||
/The comment file/i,
|
|
||||||
/could not find any license/i,
|
|
||||||
];
|
|
||||||
|
|
||||||
export function getBaseWebpackPartial(
|
|
||||||
builderOptions: BuildBuilderOptions,
|
|
||||||
extraOptions: {
|
|
||||||
esm?: boolean;
|
|
||||||
isScriptOptimizeOn?: boolean;
|
|
||||||
emitDecoratorMetadata?: boolean;
|
|
||||||
configuration?: string;
|
|
||||||
skipTypeCheck?: boolean;
|
|
||||||
}
|
|
||||||
): Configuration {
|
|
||||||
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
|
||||||
const mainFields = [
|
|
||||||
...(extraOptions.esm ? ['es2015'] : []),
|
|
||||||
'module',
|
|
||||||
'main',
|
|
||||||
];
|
|
||||||
const hashFormat = getOutputHashFormat(builderOptions.outputHashing);
|
|
||||||
const suffixFormat = extraOptions.esm ? '.esm' : '.es5';
|
|
||||||
const filename = extraOptions.isScriptOptimizeOn
|
|
||||||
? `[name]${hashFormat.script}${suffixFormat}.js`
|
|
||||||
: '[name].js';
|
|
||||||
const chunkFilename = extraOptions.isScriptOptimizeOn
|
|
||||||
? `[name]${hashFormat.chunk}${suffixFormat}.js`
|
|
||||||
: '[name].js';
|
|
||||||
const mode = extraOptions.isScriptOptimizeOn ? 'production' : 'development';
|
|
||||||
|
|
||||||
const webpackConfig: Configuration = {
|
|
||||||
target: 'web', // webpack defaults to 'browserslist' which breaks Fast Refresh
|
|
||||||
|
|
||||||
entry: {
|
|
||||||
main: [builderOptions.main],
|
|
||||||
},
|
|
||||||
devtool:
|
|
||||||
builderOptions.sourceMap === 'hidden'
|
|
||||||
? 'hidden-source-map'
|
|
||||||
: builderOptions.sourceMap
|
|
||||||
? 'source-map'
|
|
||||||
: false,
|
|
||||||
mode,
|
|
||||||
output: {
|
|
||||||
path: builderOptions.outputPath,
|
|
||||||
filename,
|
|
||||||
chunkFilename,
|
|
||||||
hashFunction: 'xxhash64',
|
|
||||||
// Disabled for performance
|
|
||||||
pathinfo: false,
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
// Enabled for performance
|
|
||||||
unsafeCache: true,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.(bmp|png|jpe?g|gif|webp|avif)$/,
|
|
||||||
type: 'asset',
|
|
||||||
parser: {
|
|
||||||
dataUrlCondition: {
|
|
||||||
maxSize: 10_000, // 10 kB
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// There's an issue resolving paths without fully specified extensions
|
|
||||||
// See: https://github.com/graphql/graphql-js/issues/2721
|
|
||||||
// TODO(jack): Add a flag to turn this option on like Next.js does via experimental flag.
|
|
||||||
// See: https://github.com/vercel/next.js/pull/29880
|
|
||||||
test: /\.m?jsx?$/,
|
|
||||||
resolve: {
|
|
||||||
fullySpecified: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
builderOptions.compiler === 'babel' && {
|
|
||||||
test: /\.([jt])sx?$/,
|
|
||||||
loader: join(__dirname, 'web-babel-loader'),
|
|
||||||
exclude: /node_modules/,
|
|
||||||
options: {
|
|
||||||
rootMode: 'upward',
|
|
||||||
cwd: join(builderOptions.root, builderOptions.sourceRoot),
|
|
||||||
emitDecoratorMetadata: extraOptions.emitDecoratorMetadata,
|
|
||||||
isModern: extraOptions.esm,
|
|
||||||
envName: extraOptions.isScriptOptimizeOn
|
|
||||||
? 'production'
|
|
||||||
: extraOptions.configuration,
|
|
||||||
babelrc: true,
|
|
||||||
cacheDirectory: true,
|
|
||||||
cacheCompression: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
builderOptions.compiler === 'swc' && {
|
|
||||||
test: /\.([jt])sx?$/,
|
|
||||||
loader: require.resolve('swc-loader'),
|
|
||||||
exclude: /node_modules/,
|
|
||||||
options: {
|
|
||||||
jsc: {
|
|
||||||
parser: {
|
|
||||||
syntax: 'typescript',
|
|
||||||
decorators: true,
|
|
||||||
tsx: true,
|
|
||||||
},
|
|
||||||
transform: {
|
|
||||||
react: {
|
|
||||||
runtime: 'automatic',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
loose: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
].filter(Boolean),
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions,
|
|
||||||
alias: getAliases(builderOptions),
|
|
||||||
plugins: [
|
|
||||||
new TsconfigPathsPlugin({
|
|
||||||
configFile: builderOptions.tsConfig,
|
|
||||||
extensions,
|
|
||||||
mainFields,
|
|
||||||
}) as never, // TODO: Remove never type when 'tsconfig-paths-webpack-plugin' types fixed
|
|
||||||
],
|
|
||||||
mainFields,
|
|
||||||
},
|
|
||||||
performance: {
|
|
||||||
hints: false,
|
|
||||||
},
|
|
||||||
plugins: [new webpack.DefinePlugin(getClientEnvironment(mode).stringified)],
|
|
||||||
watch: builderOptions.watch,
|
|
||||||
watchOptions: {
|
|
||||||
poll: builderOptions.poll,
|
|
||||||
},
|
|
||||||
stats: getStatsConfig(builderOptions),
|
|
||||||
ignoreWarnings: [
|
|
||||||
(x) =>
|
|
||||||
IGNORED_WEBPACK_WARNINGS.some((r) =>
|
|
||||||
typeof x === 'string' ? r.test(x) : r.test(x.message)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
experiments: {
|
|
||||||
cacheUnaffected: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (builderOptions.compiler !== 'swc' && extraOptions.isScriptOptimizeOn) {
|
|
||||||
webpackConfig.optimization = {
|
|
||||||
sideEffects: true,
|
|
||||||
minimizer: [
|
|
||||||
new TerserPlugin({
|
|
||||||
parallel: true,
|
|
||||||
terserOptions: {
|
|
||||||
ecma: (extraOptions.esm ? 2016 : 5) as TerserPlugin.TerserECMA,
|
|
||||||
safari10: true,
|
|
||||||
output: {
|
|
||||||
ascii_only: true,
|
|
||||||
comments: false,
|
|
||||||
webkit: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
runtimeChunk: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const extraPlugins: WebpackPluginInstance[] = [];
|
|
||||||
|
|
||||||
if (!extraOptions.skipTypeCheck && extraOptions.esm) {
|
|
||||||
extraPlugins.push(
|
|
||||||
new ForkTsCheckerWebpackPlugin({
|
|
||||||
typescript: {
|
|
||||||
configFile: builderOptions.tsConfig,
|
|
||||||
memoryLimit: builderOptions.memoryLimit || 2018,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (builderOptions.progress) {
|
|
||||||
extraPlugins.push(new webpack.ProgressPlugin());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO LicenseWebpackPlugin needs a PR for proper typing
|
|
||||||
if (builderOptions.extractLicenses) {
|
|
||||||
extraPlugins.push(
|
|
||||||
new LicenseWebpackPlugin({
|
|
||||||
stats: {
|
|
||||||
errors: false,
|
|
||||||
},
|
|
||||||
perChunkOutput: false,
|
|
||||||
outputFilename: `3rdpartylicenses.txt`,
|
|
||||||
}) as unknown as WebpackPluginInstance
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Array.isArray(builderOptions.assets) &&
|
|
||||||
builderOptions.assets.length > 0
|
|
||||||
) {
|
|
||||||
extraPlugins.push(createCopyPlugin(builderOptions.assets));
|
|
||||||
}
|
|
||||||
|
|
||||||
webpackConfig.plugins = [...webpackConfig.plugins, ...extraPlugins];
|
|
||||||
|
|
||||||
return webpackConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAliases(options: BuildBuilderOptions): { [key: string]: string } {
|
|
||||||
return options.fileReplacements.reduce(
|
|
||||||
(aliases, replacement) => ({
|
|
||||||
...aliases,
|
|
||||||
[replacement.replace]: replacement.with,
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStatsConfig(options: BuildBuilderOptions) {
|
|
||||||
return {
|
|
||||||
hash: true,
|
|
||||||
timings: false,
|
|
||||||
cached: false,
|
|
||||||
cachedAssets: false,
|
|
||||||
modules: false,
|
|
||||||
warnings: true,
|
|
||||||
errors: true,
|
|
||||||
colors: !options.verbose && !options.statsJson,
|
|
||||||
chunks: !options.verbose,
|
|
||||||
assets: !!options.verbose,
|
|
||||||
chunkOrigins: !!options.verbose,
|
|
||||||
chunkModules: !!options.verbose,
|
|
||||||
children: !!options.verbose,
|
|
||||||
reasons: !!options.verbose,
|
|
||||||
version: !!options.verbose,
|
|
||||||
errorDetails: !!options.verbose,
|
|
||||||
moduleTrace: !!options.verbose,
|
|
||||||
usedExports: !!options.verbose,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is shamelessly taken from CRA and modified for NX use
|
|
||||||
// https://github.com/facebook/create-react-app/blob/4784997f0682e75eb32a897b4ffe34d735912e6c/packages/react-scripts/config/env.js#L71
|
|
||||||
function getClientEnvironment(mode) {
|
|
||||||
// Grab NODE_ENV and NX_* environment variables and prepare them to be
|
|
||||||
// injected into the application via DefinePlugin in webpack configuration.
|
|
||||||
const NX_APP = /^NX_/i;
|
|
||||||
|
|
||||||
const raw = Object.keys(process.env)
|
|
||||||
.filter((key) => NX_APP.test(key))
|
|
||||||
.reduce(
|
|
||||||
(env, key) => {
|
|
||||||
env[key] = process.env[key];
|
|
||||||
return env;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Useful for determining whether we’re running in production mode.
|
|
||||||
NODE_ENV: process.env.NODE_ENV || mode,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Stringify all values so we can feed into webpack DefinePlugin
|
|
||||||
const stringified = {
|
|
||||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
|
||||||
env[key] = JSON.stringify(raw[key]);
|
|
||||||
return env;
|
|
||||||
}, {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
return { stringified };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createCopyPlugin(assets: AssetGlobPattern[]) {
|
|
||||||
return new CopyWebpackPlugin({
|
|
||||||
patterns: assets.map((asset) => {
|
|
||||||
return {
|
|
||||||
context: asset.input,
|
|
||||||
// Now we remove starting slash to make Webpack place it from the output root.
|
|
||||||
to: asset.output,
|
|
||||||
from: asset.glob,
|
|
||||||
globOptions: {
|
|
||||||
ignore: [
|
|
||||||
'.gitkeep',
|
|
||||||
'**/.DS_Store',
|
|
||||||
'**/Thumbs.db',
|
|
||||||
...(asset.ignore ?? []),
|
|
||||||
],
|
|
||||||
dot: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
import { getDevServerConfig } from '@nrwl/web/src/utils/devserver.config';
|
|
||||||
|
|
||||||
jest.mock('./webpack/partials/common', () => ({
|
|
||||||
getCommonConfig: () => ({
|
|
||||||
output: {},
|
|
||||||
resolve: {},
|
|
||||||
}),
|
|
||||||
getCommonPartial: () => ({}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('tsconfig-paths-webpack-plugin', () => ({
|
|
||||||
TsconfigPathsPlugin: class TsconfigPathsPlugin {},
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('getDevServerConfig', () => {
|
|
||||||
it('should set mode to production when optimization is on', () => {
|
|
||||||
const result = getDevServerConfig(
|
|
||||||
'/',
|
|
||||||
'/app',
|
|
||||||
'/app/src',
|
|
||||||
{
|
|
||||||
optimization: true,
|
|
||||||
root: '/app',
|
|
||||||
sourceRoot: '/app/src',
|
|
||||||
main: '/app/src/main.ts',
|
|
||||||
outputPath: '/dist/app',
|
|
||||||
tsConfig: '/app/tsconfig.app.json',
|
|
||||||
compiler: 'babel',
|
|
||||||
assets: [],
|
|
||||||
fileReplacements: [],
|
|
||||||
index: '/app/src/index.html',
|
|
||||||
scripts: [],
|
|
||||||
styles: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buildTarget: 'app:build:production',
|
|
||||||
allowedHosts: '',
|
|
||||||
liveReload: false,
|
|
||||||
hmr: false,
|
|
||||||
ssl: false,
|
|
||||||
watch: false,
|
|
||||||
open: false,
|
|
||||||
host: 'localhost',
|
|
||||||
port: 4200,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.mode).toEqual('production');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set mode to development when optimization is off', () => {
|
|
||||||
const result = getDevServerConfig(
|
|
||||||
'/',
|
|
||||||
'/app',
|
|
||||||
'/app/src',
|
|
||||||
{
|
|
||||||
root: '/app',
|
|
||||||
sourceRoot: '/app/src',
|
|
||||||
main: '/app/src/main.ts',
|
|
||||||
outputPath: '/dist/app',
|
|
||||||
tsConfig: '/app/tsconfig.app.json',
|
|
||||||
compiler: 'babel',
|
|
||||||
assets: [],
|
|
||||||
fileReplacements: [],
|
|
||||||
index: '/app/src/index.html',
|
|
||||||
scripts: [],
|
|
||||||
styles: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buildTarget: 'app:build:development',
|
|
||||||
allowedHosts: '',
|
|
||||||
liveReload: false,
|
|
||||||
hmr: false,
|
|
||||||
ssl: false,
|
|
||||||
watch: false,
|
|
||||||
open: false,
|
|
||||||
host: 'localhost',
|
|
||||||
port: 4200,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.mode).toEqual('development');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,67 +1 @@
|
|||||||
import * as path from 'path';
|
export * from '@nrwl/webpack/src/utils/fs';
|
||||||
import { existsSync, removeSync } from 'fs-extra';
|
|
||||||
import { directoryExists } from 'nx/src/utils/fileutils';
|
|
||||||
|
|
||||||
export function findUp(
|
|
||||||
names: string | string[],
|
|
||||||
from: string,
|
|
||||||
stopOnNodeModules = false
|
|
||||||
) {
|
|
||||||
if (!Array.isArray(names)) {
|
|
||||||
names = [names];
|
|
||||||
}
|
|
||||||
const root = path.parse(from).root;
|
|
||||||
|
|
||||||
let currentDir = from;
|
|
||||||
while (currentDir && currentDir !== root) {
|
|
||||||
for (const name of names) {
|
|
||||||
const p = path.join(currentDir, name);
|
|
||||||
if (existsSync(p)) {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stopOnNodeModules) {
|
|
||||||
const nodeModuleP = path.join(currentDir, 'node_modules');
|
|
||||||
if (existsSync(nodeModuleP)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDir = path.dirname(currentDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findAllNodeModules(from: string, root?: string) {
|
|
||||||
const nodeModules: string[] = [];
|
|
||||||
|
|
||||||
let current = from;
|
|
||||||
while (current && current !== root) {
|
|
||||||
const potential = path.join(current, 'node_modules');
|
|
||||||
if (directoryExists(potential)) {
|
|
||||||
nodeModules.push(potential);
|
|
||||||
}
|
|
||||||
|
|
||||||
const next = path.dirname(current);
|
|
||||||
if (next === current) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeModules;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an output directory, but error out if it's the root of the project.
|
|
||||||
*/
|
|
||||||
export function deleteOutputDir(root: string, outputPath: string) {
|
|
||||||
const resolvedOutputPath = path.resolve(root, outputPath);
|
|
||||||
if (resolvedOutputPath === root) {
|
|
||||||
throw new Error('Output path MUST not be project root directory!');
|
|
||||||
}
|
|
||||||
|
|
||||||
removeSync(resolvedOutputPath);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,115 +0,0 @@
|
|||||||
import { normalizeBuildOptions } from './normalize';
|
|
||||||
import { BuildBuilderOptions } from './shared-models';
|
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
|
|
||||||
describe('normalizeBuildOptions', () => {
|
|
||||||
let testOptions: BuildBuilderOptions;
|
|
||||||
let root: string;
|
|
||||||
let sourceRoot: string;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
testOptions = {
|
|
||||||
compiler: 'babel',
|
|
||||||
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,
|
|
||||||
webpackConfig: 'apps/nodeapp/webpack.config',
|
|
||||||
};
|
|
||||||
root = '/root';
|
|
||||||
sourceRoot = 'apps/nodeapp/src';
|
|
||||||
});
|
|
||||||
|
|
||||||
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', () => {
|
|
||||||
jest.spyOn(fs, 'statSync').mockReturnValue({
|
|
||||||
isDirectory: () => true,
|
|
||||||
} as any);
|
|
||||||
const result = normalizeBuildOptions(
|
|
||||||
<BuildBuilderOptions>{
|
|
||||||
...testOptions,
|
|
||||||
root,
|
|
||||||
assets: [
|
|
||||||
'apps/nodeapp/src/assets',
|
|
||||||
{
|
|
||||||
input: 'outsideproj',
|
|
||||||
output: 'output',
|
|
||||||
glob: '**/*',
|
|
||||||
ignore: ['**/*.json'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
root,
|
|
||||||
sourceRoot
|
|
||||||
);
|
|
||||||
expect(result.assets).toEqual([
|
|
||||||
{
|
|
||||||
input: '/root/apps/nodeapp/src/assets',
|
|
||||||
output: 'assets',
|
|
||||||
glob: '**/*',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '/root/outsideproj',
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve both node modules and relative path for webpackConfig', () => {
|
|
||||||
let result = normalizeBuildOptions(testOptions, root, sourceRoot);
|
|
||||||
expect(result.webpackConfig).toEqual('/root/apps/nodeapp/webpack.config');
|
|
||||||
|
|
||||||
result = normalizeBuildOptions(
|
|
||||||
{
|
|
||||||
...testOptions,
|
|
||||||
webpackConfig: 'react', // something that exists in node_modules
|
|
||||||
},
|
|
||||||
root,
|
|
||||||
sourceRoot
|
|
||||||
);
|
|
||||||
expect(result.webpackConfig).toMatch('react');
|
|
||||||
expect(result.webpackConfig).not.toMatch(root);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,173 +1 @@
|
|||||||
import { WebWebpackExecutorOptions } from '../executors/webpack/webpack.impl';
|
export * from '@nrwl/webpack/src/executors/webpack/lib/normalize-options';
|
||||||
import { normalizePath } from '@nrwl/devkit';
|
|
||||||
import { basename, dirname, relative, resolve } from 'path';
|
|
||||||
import {
|
|
||||||
AssetGlobPattern,
|
|
||||||
BuildBuilderOptions,
|
|
||||||
ExtraEntryPoint,
|
|
||||||
ExtraEntryPointClass,
|
|
||||||
} from './shared-models';
|
|
||||||
import { statSync } from 'fs';
|
|
||||||
|
|
||||||
export interface FileReplacement {
|
|
||||||
replace: string;
|
|
||||||
with: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeBuildOptions<T extends BuildBuilderOptions>(
|
|
||||||
options: T,
|
|
||||||
root: string,
|
|
||||||
sourceRoot: string
|
|
||||||
): T {
|
|
||||||
return {
|
|
||||||
...options,
|
|
||||||
root,
|
|
||||||
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: normalizePluginPath(options.webpackConfig, root),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizePluginPath(pluginPath: void | string, root: string) {
|
|
||||||
if (!pluginPath) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require.resolve(pluginPath);
|
|
||||||
} catch {
|
|
||||||
return resolve(root, pluginPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeAssets(
|
|
||||||
assets: any[],
|
|
||||||
root: string,
|
|
||||||
sourceRoot: string
|
|
||||||
): AssetGlobPattern[] {
|
|
||||||
return assets.map((asset) => {
|
|
||||||
if (typeof asset === 'string') {
|
|
||||||
const assetPath = normalizePath(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.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const assetPath = normalizePath(asset.input);
|
|
||||||
const resolvedAssetPath = resolve(root, assetPath);
|
|
||||||
return {
|
|
||||||
...asset,
|
|
||||||
input: resolvedAssetPath,
|
|
||||||
// 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),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeWebBuildOptions(
|
|
||||||
options: WebWebpackExecutorOptions,
|
|
||||||
root: string,
|
|
||||||
sourceRoot: string
|
|
||||||
): WebWebpackExecutorOptions {
|
|
||||||
return {
|
|
||||||
...normalizeBuildOptions(options, root, sourceRoot),
|
|
||||||
optimization:
|
|
||||||
typeof options.optimization !== 'object'
|
|
||||||
? {
|
|
||||||
scripts: options.optimization,
|
|
||||||
styles: options.optimization,
|
|
||||||
}
|
|
||||||
: options.optimization,
|
|
||||||
polyfills: options.polyfills ? resolve(root, options.polyfills) : undefined,
|
|
||||||
es2015Polyfills: options.es2015Polyfills
|
|
||||||
? resolve(root, options.es2015Polyfills)
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertBuildOptions(
|
|
||||||
buildOptions: WebWebpackExecutorOptions
|
|
||||||
): any {
|
|
||||||
const options = buildOptions as any;
|
|
||||||
return <any>{
|
|
||||||
...options,
|
|
||||||
buildOptimizer: options.optimization,
|
|
||||||
forkTypeChecker: false,
|
|
||||||
lazyModules: [] as string[],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NormalizedEntryPoint = Required<Omit<ExtraEntryPointClass, 'lazy'>>;
|
|
||||||
|
|
||||||
export function normalizeExtraEntryPoints(
|
|
||||||
extraEntryPoints: ExtraEntryPoint[],
|
|
||||||
defaultBundleName: string
|
|
||||||
): NormalizedEntryPoint[] {
|
|
||||||
return extraEntryPoints.map((entry) => {
|
|
||||||
let normalizedEntry;
|
|
||||||
if (typeof entry === 'string') {
|
|
||||||
normalizedEntry = {
|
|
||||||
input: entry,
|
|
||||||
inject: true,
|
|
||||||
bundleName: defaultBundleName,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const { lazy, inject = true, ...newEntry } = entry;
|
|
||||||
const injectNormalized = entry.lazy !== undefined ? !entry.lazy : inject;
|
|
||||||
let bundleName;
|
|
||||||
|
|
||||||
if (entry.bundleName) {
|
|
||||||
bundleName = entry.bundleName;
|
|
||||||
} else if (!injectNormalized) {
|
|
||||||
// Lazy entry points use the file name as bundle name.
|
|
||||||
bundleName = basename(
|
|
||||||
normalizePath(
|
|
||||||
entry.input.replace(/\.(js|css|scss|sass|less|styl)$/i, '')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
bundleName = defaultBundleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizedEntry = { ...newEntry, inject: injectNormalized, bundleName };
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalizedEntry;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,120 +0,0 @@
|
|||||||
import * as webpack from 'webpack';
|
|
||||||
import type { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { extname } from 'path';
|
|
||||||
|
|
||||||
export function runWebpack(config: webpack.Configuration): Observable<any> {
|
|
||||||
return new Observable((subscriber) => {
|
|
||||||
// Passing `watch` option here will result in a warning due to missing callback.
|
|
||||||
// We manually call `.watch` or `.run` later so this option isn't needed here.
|
|
||||||
const { watch, ...normalizedConfig } = config;
|
|
||||||
const webpackCompiler = webpack(normalizedConfig);
|
|
||||||
|
|
||||||
const callback = (err: Error, stats: webpack.Stats) => {
|
|
||||||
if (err) {
|
|
||||||
subscriber.error(err);
|
|
||||||
}
|
|
||||||
subscriber.next(stats);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.watch) {
|
|
||||||
const watchOptions = config.watchOptions || {};
|
|
||||||
const watching = webpackCompiler.watch(watchOptions, callback);
|
|
||||||
|
|
||||||
return () => watching.close(() => subscriber.complete());
|
|
||||||
} else {
|
|
||||||
webpackCompiler.run((err, stats) => {
|
|
||||||
callback(err, stats);
|
|
||||||
webpackCompiler.close((closeErr) => {
|
|
||||||
if (closeErr) subscriber.error(closeErr);
|
|
||||||
subscriber.complete();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function runWebpackDevServer(
|
|
||||||
config: any,
|
|
||||||
webpack: typeof import('webpack'),
|
|
||||||
WebpackDevServer: typeof import('webpack-dev-server')
|
|
||||||
): Observable<{ stats: any; baseUrl: string }> {
|
|
||||||
return new Observable((subscriber) => {
|
|
||||||
const webpackCompiler: any = webpack(config);
|
|
||||||
|
|
||||||
let baseUrl: string;
|
|
||||||
|
|
||||||
webpackCompiler.hooks.done.tap('build-webpack', (stats) => {
|
|
||||||
subscriber.next({ stats, baseUrl });
|
|
||||||
});
|
|
||||||
|
|
||||||
const devServerConfig = (config as any).devServer || {};
|
|
||||||
|
|
||||||
const originalOnListen = devServerConfig.onListening;
|
|
||||||
|
|
||||||
devServerConfig.onListening = function (server: any) {
|
|
||||||
originalOnListen(server);
|
|
||||||
|
|
||||||
const devServerOptions: WebpackDevServerConfiguration = server.options;
|
|
||||||
|
|
||||||
baseUrl = `${server.options.https ? 'https' : 'http'}://${
|
|
||||||
server.options.host
|
|
||||||
}:${server.options.port}${devServerOptions.devMiddleware.publicPath}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const webpackServer = new WebpackDevServer(
|
|
||||||
devServerConfig,
|
|
||||||
webpackCompiler as any
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
webpackServer.start().catch((err) => subscriber.error(err));
|
|
||||||
return () => webpackServer.stop();
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error('Could not start start dev server');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EmittedFile {
|
|
||||||
id?: string;
|
|
||||||
name?: string;
|
|
||||||
file: string;
|
|
||||||
extension: string;
|
|
||||||
initial: boolean;
|
|
||||||
asset?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getEmittedFiles(stats: webpack.Stats) {
|
|
||||||
const { compilation } = stats;
|
|
||||||
const files: EmittedFile[] = [];
|
|
||||||
// adds all chunks to the list of emitted files such as lazy loaded modules
|
|
||||||
for (const chunk of compilation.chunks) {
|
|
||||||
for (const file of chunk.files) {
|
|
||||||
files.push({
|
|
||||||
// The id is guaranteed to exist at this point in the compilation process
|
|
||||||
// tslint:disable-next-line: no-non-null-assertion
|
|
||||||
id: chunk.id.toString(),
|
|
||||||
name: chunk.name,
|
|
||||||
file,
|
|
||||||
extension: extname(file),
|
|
||||||
initial: chunk.isOnlyInitial(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// other all files
|
|
||||||
for (const file of Object.keys(compilation.assets)) {
|
|
||||||
files.push({
|
|
||||||
file,
|
|
||||||
extension: extname(file),
|
|
||||||
initial: false,
|
|
||||||
asset: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// dedupe
|
|
||||||
return files.filter(
|
|
||||||
({ file, name }, index) =>
|
|
||||||
files.findIndex((f) => f.file === file && (!name || name === f.name)) ===
|
|
||||||
index
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
import { WebWebpackExecutorOptions } from '../executors/webpack/webpack.impl';
|
|
||||||
import { FileReplacement } from './normalize';
|
|
||||||
|
|
||||||
export interface OptimizationOptions {
|
|
||||||
scripts: boolean;
|
|
||||||
styles: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BuildBuilderOptions {
|
|
||||||
main: string;
|
|
||||||
outputPath: string;
|
|
||||||
compiler: 'babel' | 'swc';
|
|
||||||
tsConfig: string;
|
|
||||||
watch?: boolean;
|
|
||||||
sourceMap?: boolean | 'hidden';
|
|
||||||
optimization?: boolean | OptimizationOptions;
|
|
||||||
memoryLimit?: number;
|
|
||||||
maxWorkers?: number;
|
|
||||||
poll?: number;
|
|
||||||
|
|
||||||
fileReplacements?: FileReplacement[];
|
|
||||||
assets?: any[];
|
|
||||||
|
|
||||||
progress?: boolean;
|
|
||||||
statsJson?: boolean;
|
|
||||||
extractLicenses?: boolean;
|
|
||||||
verbose?: boolean;
|
|
||||||
|
|
||||||
outputHashing?: any;
|
|
||||||
webpackConfig?: string;
|
|
||||||
|
|
||||||
root?: string;
|
|
||||||
sourceRoot?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AssetGlobPattern {
|
|
||||||
glob: string;
|
|
||||||
input: string;
|
|
||||||
output: string;
|
|
||||||
ignore?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AssetPattern = AssetPatternClass | string;
|
|
||||||
|
|
||||||
export interface AssetPatternClass {
|
|
||||||
glob: string;
|
|
||||||
ignore?: string[];
|
|
||||||
input: string;
|
|
||||||
output: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Type {
|
|
||||||
All = 'all',
|
|
||||||
AllScript = 'allScript',
|
|
||||||
Any = 'any',
|
|
||||||
AnyComponentStyle = 'anyComponentStyle',
|
|
||||||
AnyScript = 'anyScript',
|
|
||||||
Bundle = 'bundle',
|
|
||||||
Initial = 'initial',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CrossOrigin {
|
|
||||||
Anonymous = 'anonymous',
|
|
||||||
None = 'none',
|
|
||||||
UseCredentials = 'use-credentials',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IndexUnion = IndexObject | string;
|
|
||||||
|
|
||||||
export interface IndexObject {
|
|
||||||
input: string;
|
|
||||||
output?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Localize = string[] | boolean;
|
|
||||||
|
|
||||||
export type OptimizationUnion = boolean | OptimizationClass;
|
|
||||||
|
|
||||||
export interface OptimizationClass {
|
|
||||||
scripts?: boolean;
|
|
||||||
styles?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum OutputHashing {
|
|
||||||
All = 'all',
|
|
||||||
Bundles = 'bundles',
|
|
||||||
Media = 'media',
|
|
||||||
None = 'none',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ExtraEntryPoint = ExtraEntryPointClass | string;
|
|
||||||
|
|
||||||
export interface ExtraEntryPointClass {
|
|
||||||
bundleName?: string;
|
|
||||||
inject?: boolean;
|
|
||||||
input: string;
|
|
||||||
lazy?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SourceMapUnion = boolean | SourceMapClass;
|
|
||||||
|
|
||||||
export interface SourceMapClass {
|
|
||||||
hidden?: boolean;
|
|
||||||
scripts?: boolean;
|
|
||||||
styles?: boolean;
|
|
||||||
vendor?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StylePreprocessorOptions {
|
|
||||||
includePaths?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WebpackConfigOptions<T = WebWebpackExecutorOptions> {
|
|
||||||
root: string;
|
|
||||||
projectRoot: string;
|
|
||||||
sourceRoot?: string;
|
|
||||||
buildOptions: T;
|
|
||||||
tsConfig: any;
|
|
||||||
tsConfigPath: string;
|
|
||||||
supportES2015: boolean;
|
|
||||||
}
|
|
||||||
25
packages/webpack/.eslintrc.json
Normal file
25
packages/webpack/.eslintrc.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["./package.json", "./generators.json", "./executors.json"],
|
||||||
|
"parser": "jsonc-eslint-parser",
|
||||||
|
"rules": {
|
||||||
|
"@nrwl/nx/nx-plugin-checks": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
13
packages/webpack/README.md
Normal file
13
packages/webpack/README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
|
||||||
|
|
||||||
|
{{links}}
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
# Nx: Smart, Fast and Extensible Build System
|
||||||
|
|
||||||
|
Nx is a next generation build system with first class monorepo support and powerful integrations.
|
||||||
|
|
||||||
|
This package is a [Webpack plugin for Nx](https://nx.dev/packages/webpack).
|
||||||
|
|
||||||
|
{{content}}
|
||||||
26
packages/webpack/executors.json
Normal file
26
packages/webpack/executors.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"builders": {
|
||||||
|
"webpack": {
|
||||||
|
"implementation": "./src/executors/webpack/compat",
|
||||||
|
"schema": "./src/executors/webpack/schema.json",
|
||||||
|
"description": "Run webpack build."
|
||||||
|
},
|
||||||
|
"dev-server": {
|
||||||
|
"implementation": "./src/executors/dev-server/compat",
|
||||||
|
"schema": "./src/executors/dev-server/schema.json",
|
||||||
|
"description": "Serve a web application."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"executors": {
|
||||||
|
"webpack": {
|
||||||
|
"implementation": "./src/executors/webpack/webpack.impl",
|
||||||
|
"schema": "./src/executors/webpack/schema.json",
|
||||||
|
"description": "Run webpack build."
|
||||||
|
},
|
||||||
|
"dev-server": {
|
||||||
|
"implementation": "./src/executors/dev-server/dev-server.impl",
|
||||||
|
"schema": "./src/executors/dev-server/schema.json",
|
||||||
|
"description": "Serve a web application."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
packages/webpack/generators.json
Normal file
33
packages/webpack/generators.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "Nx Webpack",
|
||||||
|
"version": "0.1",
|
||||||
|
"schematics": {
|
||||||
|
"init": {
|
||||||
|
"factory": "./src/generators/init/init#webpackInitSchematic",
|
||||||
|
"schema": "./src/generators/init/schema.json",
|
||||||
|
"description": "Initialize the `@nrwl/webpack` plugin.",
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"webpack-project": {
|
||||||
|
"factory": "./src/generators/webpack-project/webpack-project#webpackProjectSchematic",
|
||||||
|
"schema": "./src/generators/webpack-project/schema.json",
|
||||||
|
"description": "Add webpack configuration to a project.",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"generators": {
|
||||||
|
"init": {
|
||||||
|
"factory": "./src/generators/init/init#webpackInitGenerator",
|
||||||
|
"schema": "./src/generators/init/schema.json",
|
||||||
|
"description": "Initialize the `@nrwl/webpack` plugin.",
|
||||||
|
"aliases": ["ng-add"],
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"webpack-project": {
|
||||||
|
"factory": "./src/generators/webpack-project/webpack-project#webpackProjectGenerator",
|
||||||
|
"schema": "./src/generators/webpack-project/schema.json",
|
||||||
|
"description": "Add webpack configuration to a project.",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/webpack/index.ts
Normal file
7
packages/webpack/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export * from './src/utils/config';
|
||||||
|
export * from './src/generators/webpack-project/webpack-project';
|
||||||
|
export * from './src/executors/dev-server/schema';
|
||||||
|
export * from './src/executors/dev-server/dev-server.impl';
|
||||||
|
export * from './src/executors/webpack/lib/normalize-options';
|
||||||
|
export * from './src/executors/webpack/schema';
|
||||||
|
export * from './src/executors/webpack/webpack.impl';
|
||||||
11
packages/webpack/jest.config.ts
Normal file
11
packages/webpack/jest.config.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||||
|
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
|
||||||
|
displayName: 'webpack',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
};
|
||||||
79
packages/webpack/package.json
Normal file
79
packages/webpack/package.json
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"name": "@nrwl/webpack",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "The Nx Plugin for Webpack contains executors and generators that support building applications using Webpack",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nrwl/nx.git",
|
||||||
|
"directory": "packages/webpack"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"Monorepo",
|
||||||
|
"Webpack",
|
||||||
|
"Web",
|
||||||
|
"CLI"
|
||||||
|
],
|
||||||
|
"main": "./index.js",
|
||||||
|
"typings": "./index.d.ts",
|
||||||
|
"author": "Victor Savkin",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/nrwl/nx/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://nx.dev",
|
||||||
|
"schematics": "./generators.json",
|
||||||
|
"builders": "./executors.json",
|
||||||
|
"ng-update": {
|
||||||
|
"requirements": {},
|
||||||
|
"migrations": "./migrations.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nrwl/devkit": "file:../devkit",
|
||||||
|
"@nrwl/js": "file:../js",
|
||||||
|
"@nrwl/workspace": "file:../workspace",
|
||||||
|
"autoprefixer": "^10.4.9",
|
||||||
|
"babel-loader": "^8.2.2",
|
||||||
|
"browserslist": "^4.16.6",
|
||||||
|
"caniuse-lite": "^1.0.30001394",
|
||||||
|
"chalk": "4.1.0",
|
||||||
|
"chokidar": "^3.5.1",
|
||||||
|
"copy-webpack-plugin": "^10.2.4",
|
||||||
|
"css-loader": "^6.4.0",
|
||||||
|
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"fork-ts-checker-webpack-plugin": "7.2.13",
|
||||||
|
"fs-extra": "^10.1.0",
|
||||||
|
"ignore": "^5.0.4",
|
||||||
|
"less": "3.12.2",
|
||||||
|
"less-loader": "^10.1.0",
|
||||||
|
"license-webpack-plugin": "^4.0.2",
|
||||||
|
"loader-utils": "1.2.3",
|
||||||
|
"mini-css-extract-plugin": "~2.4.7",
|
||||||
|
"parse5": "4.0.0",
|
||||||
|
"parse5-html-rewriting-stream": "6.0.1",
|
||||||
|
"postcss": "^8.4.14",
|
||||||
|
"postcss-import": "~14.1.0",
|
||||||
|
"postcss-loader": "^6.1.1",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
|
"rxjs": "^6.5.4",
|
||||||
|
"sass": "^1.42.1",
|
||||||
|
"sass-loader": "^12.2.0",
|
||||||
|
"source-map": "0.7.3",
|
||||||
|
"source-map-loader": "^3.0.0",
|
||||||
|
"style-loader": "^3.3.0",
|
||||||
|
"stylus": "^0.55.0",
|
||||||
|
"stylus-loader": "^6.2.0",
|
||||||
|
"terser-webpack-plugin": "^5.3.3",
|
||||||
|
"ts-loader": "^9.3.1",
|
||||||
|
"ts-node": "10.9.1",
|
||||||
|
"tsconfig-paths": "^3.9.0",
|
||||||
|
"tsconfig-paths-webpack-plugin": "3.5.2",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"webpack": "^5.58.1",
|
||||||
|
"webpack-dev-server": "^4.9.3",
|
||||||
|
"webpack-merge": "^5.8.0",
|
||||||
|
"webpack-node-externals": "^3.0.0",
|
||||||
|
"webpack-sources": "^3.2.3",
|
||||||
|
"webpack-subresource-integrity": "^5.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
87
packages/webpack/project.json
Normal file
87
packages/webpack/project.json
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "packages/webpack",
|
||||||
|
"projectType": "library",
|
||||||
|
"targets": {
|
||||||
|
"test": {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "packages/webpack/jest.config.ts",
|
||||||
|
"passWithNoTests": true
|
||||||
|
},
|
||||||
|
"outputs": ["coverage/packages/webpack"]
|
||||||
|
},
|
||||||
|
"build-base": {
|
||||||
|
"executor": "@nrwl/js:tsc",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "build/packages/webpack",
|
||||||
|
"tsConfig": "packages/webpack/tsconfig.lib.json",
|
||||||
|
"main": "packages/webpack/index.ts",
|
||||||
|
"updateBuildableProjectDepsInPackageJson": false,
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"input": "packages/webpack",
|
||||||
|
"glob": "**/files/**",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/webpack",
|
||||||
|
"glob": "**/files/**/.gitkeep",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/webpack",
|
||||||
|
"glob": "**/*.json",
|
||||||
|
"ignore": ["**/tsconfig*.json", "project.json", ".eslintrc.json"],
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/webpack",
|
||||||
|
"glob": "**/*.js",
|
||||||
|
"ignore": ["**/jest.config.js"],
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/webpack",
|
||||||
|
"glob": "**/*.d.ts",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "",
|
||||||
|
"glob": "LICENSE",
|
||||||
|
"output": "/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"outputs": ["{options.outputPath}"]
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"outputs": ["build/packages/webpack"],
|
||||||
|
"options": {
|
||||||
|
"command": "node ./scripts/copy-readme.js webpack"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nrwl/linter:eslint",
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": [
|
||||||
|
"packages/webpack/**/*.ts",
|
||||||
|
"packages/webpack/**/*.spec.ts",
|
||||||
|
"packages/webpack/**/*_spec.ts",
|
||||||
|
"packages/webpack/**/*.spec.tsx",
|
||||||
|
"packages/webpack/**/*.spec.js",
|
||||||
|
"packages/webpack/**/*.spec.jsx",
|
||||||
|
"packages/webpack/**/*.d.ts",
|
||||||
|
"packages/webpack/**/executors/**/schema.json",
|
||||||
|
"packages/webpack/**/generators/**/schema.json",
|
||||||
|
"packages/webpack/generators.json",
|
||||||
|
"packages/webpack/executors.json",
|
||||||
|
"packages/webpack/package.json",
|
||||||
|
"packages/webpack/migrations.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"outputs": ["{options.outputFile}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/webpack/src/executors/dev-server/compat.ts
Normal file
5
packages/webpack/src/executors/dev-server/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import devServerExecutor from './dev-server.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(devServerExecutor);
|
||||||
119
packages/webpack/src/executors/dev-server/dev-server.impl.ts
Normal file
119
packages/webpack/src/executors/dev-server/dev-server.impl.ts
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import * as webpack from 'webpack';
|
||||||
|
import {
|
||||||
|
ExecutorContext,
|
||||||
|
parseTargetString,
|
||||||
|
readTargetOptions,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import { eachValueFrom } from '@nrwl/devkit/src/utils/rxjs-for-await';
|
||||||
|
import { map, tap } from 'rxjs/operators';
|
||||||
|
import * as WebpackDevServer from 'webpack-dev-server';
|
||||||
|
|
||||||
|
import { getDevServerConfig } from './lib/get-dev-server-config';
|
||||||
|
import {
|
||||||
|
calculateProjectDependencies,
|
||||||
|
createTmpTsConfig,
|
||||||
|
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
|
import { runWebpackDevServer } from '../../utils/run-webpack';
|
||||||
|
import { resolveCustomWebpackConfig } from '../../utils/webpack/custom-webpack';
|
||||||
|
import { normalizeOptions } from '../webpack/lib/normalize-options';
|
||||||
|
import { getEmittedFiles } from '../webpack/lib/get-emitted-files';
|
||||||
|
import { WebpackExecutorOptions } from '../webpack/schema';
|
||||||
|
import { WebDevServerOptions } from './schema';
|
||||||
|
|
||||||
|
export async function* devServerExecutor(
|
||||||
|
serveOptions: WebDevServerOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
const { root: projectRoot, sourceRoot } =
|
||||||
|
context.workspace.projects[context.projectName];
|
||||||
|
const buildOptions = normalizeOptions(
|
||||||
|
getBuildOptions(serveOptions, context),
|
||||||
|
context.root,
|
||||||
|
sourceRoot
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!buildOptions.index) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot run dev-server without "index" option. Check the build options for ${context.projectName}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buildOptions.buildLibsFromSource) {
|
||||||
|
const { target, dependencies } = calculateProjectDependencies(
|
||||||
|
context.projectGraph,
|
||||||
|
context.root,
|
||||||
|
context.projectName,
|
||||||
|
'build', // should be generalized
|
||||||
|
context.configurationName
|
||||||
|
);
|
||||||
|
buildOptions.tsConfig = createTmpTsConfig(
|
||||||
|
buildOptions.tsConfig,
|
||||||
|
context.root,
|
||||||
|
target.data.root,
|
||||||
|
dependencies
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let webpackConfig = getDevServerConfig(context, buildOptions, serveOptions);
|
||||||
|
|
||||||
|
if (buildOptions.webpackConfig) {
|
||||||
|
let customWebpack = resolveCustomWebpackConfig(
|
||||||
|
buildOptions.webpackConfig,
|
||||||
|
buildOptions.tsConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof customWebpack.then === 'function') {
|
||||||
|
customWebpack = await customWebpack;
|
||||||
|
}
|
||||||
|
|
||||||
|
webpackConfig = await customWebpack(webpackConfig, {
|
||||||
|
buildOptions,
|
||||||
|
configuration: serveOptions.buildTarget.split(':')[2],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return yield* eachValueFrom(
|
||||||
|
runWebpackDevServer(webpackConfig, webpack, WebpackDevServer).pipe(
|
||||||
|
tap(({ stats }) => {
|
||||||
|
console.info(stats.toString((webpackConfig as any).stats));
|
||||||
|
}),
|
||||||
|
map(({ baseUrl, stats }) => {
|
||||||
|
return {
|
||||||
|
baseUrl,
|
||||||
|
emittedFiles: getEmittedFiles(stats),
|
||||||
|
success: !stats.hasErrors(),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBuildOptions(
|
||||||
|
options: WebDevServerOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): WebpackExecutorOptions {
|
||||||
|
const target = parseTargetString(options.buildTarget);
|
||||||
|
|
||||||
|
const overrides: Partial<WebpackExecutorOptions> = {
|
||||||
|
watch: false,
|
||||||
|
};
|
||||||
|
if (options.maxWorkers) {
|
||||||
|
overrides.maxWorkers = options.maxWorkers;
|
||||||
|
}
|
||||||
|
if (options.memoryLimit) {
|
||||||
|
overrides.memoryLimit = options.memoryLimit;
|
||||||
|
}
|
||||||
|
if (options.baseHref) {
|
||||||
|
overrides.baseHref = options.baseHref;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildOptions = readTargetOptions(target, context);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...buildOptions,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default devServerExecutor;
|
||||||
@ -1,29 +1,27 @@
|
|||||||
import { logger } from '@nrwl/devkit';
|
import { ExecutorContext, logger } from '@nrwl/devkit';
|
||||||
import type { Configuration as WebpackConfiguration } from 'webpack';
|
import type { Configuration as WebpackConfiguration } from 'webpack';
|
||||||
import type { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
|
import type { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { basename, resolve } from 'path';
|
import { basename, resolve } from 'path';
|
||||||
|
|
||||||
import { getWebConfig } from './web.config';
|
import { getWebpackConfig } from '../../webpack/lib/get-webpack-config';
|
||||||
import { WebWebpackExecutorOptions } from '../executors/webpack/webpack.impl';
|
import { WebDevServerOptions } from '../schema';
|
||||||
import { WebDevServerOptions } from '../executors/dev-server/dev-server.impl';
|
|
||||||
import { buildServePath } from './serve-path';
|
import { buildServePath } from './serve-path';
|
||||||
import { OptimizationOptions } from './shared-models';
|
|
||||||
import { readFileSync } from 'fs-extra';
|
import { readFileSync } from 'fs-extra';
|
||||||
import { IndexHtmlWebpackPlugin } from './/webpack/plugins/index-html-webpack-plugin';
|
import { generateEntryPoints } from '../../../utils//webpack/package-chunk-sort';
|
||||||
import { generateEntryPoints } from './webpack/package-chunk-sort';
|
import { IndexHtmlWebpackPlugin } from '../../../utils/webpack/plugins/index-html-webpack-plugin';
|
||||||
|
import { NormalizedWebpackExecutorOptions } from '../../webpack/schema';
|
||||||
|
|
||||||
export function getDevServerConfig(
|
export function getDevServerConfig(
|
||||||
workspaceRoot: string,
|
context: ExecutorContext,
|
||||||
projectRoot: string,
|
buildOptions: NormalizedWebpackExecutorOptions,
|
||||||
sourceRoot: string,
|
|
||||||
buildOptions: WebWebpackExecutorOptions,
|
|
||||||
serveOptions: WebDevServerOptions
|
serveOptions: WebDevServerOptions
|
||||||
): Partial<WebpackConfiguration> {
|
): Partial<WebpackConfiguration> {
|
||||||
const webpackConfig = getWebConfig(
|
const workspaceRoot = context.root;
|
||||||
workspaceRoot,
|
const { root: projectRoot, sourceRoot } =
|
||||||
projectRoot,
|
context.workspace.projects[context.projectName];
|
||||||
sourceRoot,
|
const webpackConfig = getWebpackConfig(
|
||||||
|
context,
|
||||||
buildOptions,
|
buildOptions,
|
||||||
true,
|
true,
|
||||||
typeof buildOptions.optimization === 'boolean'
|
typeof buildOptions.optimization === 'boolean'
|
||||||
@ -65,12 +63,20 @@ export function getDevServerConfig(
|
|||||||
function getDevServerPartial(
|
function getDevServerPartial(
|
||||||
root: string,
|
root: string,
|
||||||
options: WebDevServerOptions,
|
options: WebDevServerOptions,
|
||||||
buildOptions: WebWebpackExecutorOptions
|
buildOptions: NormalizedWebpackExecutorOptions
|
||||||
): WebpackDevServerConfiguration {
|
): WebpackDevServerConfiguration {
|
||||||
const servePath = buildServePath(buildOptions);
|
const servePath = buildServePath(buildOptions);
|
||||||
|
|
||||||
const { scripts: scriptsOptimization, styles: stylesOptimization } =
|
let scriptsOptimization: boolean;
|
||||||
(buildOptions.optimization || {}) as OptimizationOptions;
|
let stylesOptimization: boolean;
|
||||||
|
if (typeof buildOptions.optimization === 'boolean') {
|
||||||
|
scriptsOptimization = stylesOptimization = buildOptions.optimization;
|
||||||
|
} else if (buildOptions.optimization) {
|
||||||
|
scriptsOptimization = buildOptions.optimization.scripts;
|
||||||
|
stylesOptimization = buildOptions.optimization.styles;
|
||||||
|
} else {
|
||||||
|
scriptsOptimization = stylesOptimization = false;
|
||||||
|
}
|
||||||
|
|
||||||
const config: WebpackDevServerConfiguration = {
|
const config: WebpackDevServerConfiguration = {
|
||||||
host: options.host,
|
host: options.host,
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import { WebWebpackExecutorOptions } from '../executors/webpack/webpack.impl';
|
import type { NormalizedWebpackExecutorOptions } from '../../webpack/schema';
|
||||||
|
|
||||||
export function buildServePath(browserOptions: WebWebpackExecutorOptions) {
|
export function buildServePath(
|
||||||
|
browserOptions: NormalizedWebpackExecutorOptions
|
||||||
|
) {
|
||||||
let servePath =
|
let servePath =
|
||||||
_findDefaultServePath(browserOptions.baseHref, browserOptions.deployUrl) ||
|
_findDefaultServePath(browserOptions.baseHref, browserOptions.deployUrl) ||
|
||||||
'/';
|
'/';
|
||||||
18
packages/webpack/src/executors/dev-server/schema.d.ts
vendored
Normal file
18
packages/webpack/src/executors/dev-server/schema.d.ts
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export interface WebDevServerOptions {
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
publicHost?: string;
|
||||||
|
ssl: boolean;
|
||||||
|
sslKey?: string;
|
||||||
|
sslCert?: string;
|
||||||
|
proxyConfig?: string;
|
||||||
|
buildTarget: string;
|
||||||
|
open: boolean;
|
||||||
|
liveReload: boolean;
|
||||||
|
hmr: boolean;
|
||||||
|
watch: boolean;
|
||||||
|
allowedHosts: string;
|
||||||
|
maxWorkers?: number;
|
||||||
|
memoryLimit?: number;
|
||||||
|
baseHref?: string;
|
||||||
|
}
|
||||||
75
packages/webpack/src/executors/dev-server/schema.json
Normal file
75
packages/webpack/src/executors/dev-server/schema.json
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"title": "Web Dev Server",
|
||||||
|
"description": "Serve a web application.",
|
||||||
|
"cli": "nx",
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"hmr": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable hot module replacement.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"publicHost": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public URL where the application will be served."
|
||||||
|
},
|
||||||
|
"open": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Open the application in the browser.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"allowedHosts": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "This option allows you to whitelist services that are allowed to access the dev server."
|
||||||
|
},
|
||||||
|
"memoryLimit": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Memory limit for type checking service process in `MB`."
|
||||||
|
},
|
||||||
|
"maxWorkers": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Number of workers to use for type checking."
|
||||||
|
},
|
||||||
|
"baseHref": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Base url for the application being built."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/webpack/src/executors/webpack/compat.ts
Normal file
5
packages/webpack/src/executors/webpack/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import { webpackExecutor } from './webpack.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(webpackExecutor);
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import type { Stats } from 'webpack';
|
||||||
|
import { extname } from 'path';
|
||||||
|
|
||||||
|
import { EmittedFile } from '../../../utils/models';
|
||||||
|
|
||||||
|
export function getEmittedFiles(stats: Stats): EmittedFile[] {
|
||||||
|
const { compilation } = stats;
|
||||||
|
const files: EmittedFile[] = [];
|
||||||
|
// adds all chunks to the list of emitted files such as lazy loaded modules
|
||||||
|
for (const chunk of compilation.chunks) {
|
||||||
|
for (const file of chunk.files) {
|
||||||
|
files.push({
|
||||||
|
// The id is guaranteed to exist at this point in the compilation process
|
||||||
|
// tslint:disable-next-line: no-non-null-assertion
|
||||||
|
id: chunk.id.toString(),
|
||||||
|
name: chunk.name,
|
||||||
|
file,
|
||||||
|
extension: extname(file),
|
||||||
|
initial: chunk.isOnlyInitial(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// other all files
|
||||||
|
for (const file of Object.keys(compilation.assets)) {
|
||||||
|
files.push({
|
||||||
|
file,
|
||||||
|
extension: extname(file),
|
||||||
|
initial: false,
|
||||||
|
asset: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// dedupe
|
||||||
|
return files.filter(
|
||||||
|
({ file, name }, index) =>
|
||||||
|
files.findIndex((f) => f.file === file && (!name || name === f.name)) ===
|
||||||
|
index
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -3,16 +3,16 @@ import { posix, resolve } from 'path';
|
|||||||
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
||||||
import { ScriptTarget } from 'typescript';
|
import { ScriptTarget } from 'typescript';
|
||||||
import { getHashDigest, interpolateName } from 'loader-utils';
|
import { getHashDigest, interpolateName } from 'loader-utils';
|
||||||
import { Configuration } from 'webpack';
|
import type { Configuration } from 'webpack';
|
||||||
|
|
||||||
import { WebWebpackExecutorOptions } from '../executors/webpack/webpack.impl';
|
import { NormalizedWebpackExecutorOptions } from '../schema';
|
||||||
import { convertBuildOptions } from './normalize';
|
|
||||||
|
|
||||||
// TODO(jack): These should be inlined in a single function so it is easier to understand
|
// TODO(jack): These should be inlined in a single function so it is easier to understand
|
||||||
import { getBaseWebpackPartial } from './config';
|
import { getBaseWebpackPartial } from '../../../utils/config';
|
||||||
import { getBrowserConfig } from './webpack/partials/browser';
|
import { getBrowserConfig } from '../../../utils/webpack/partials/browser';
|
||||||
import { getCommonConfig } from './webpack/partials/common';
|
import { getCommonConfig } from '../../../utils/webpack/partials/common';
|
||||||
import { getStylesConfig } from './webpack/partials/styles';
|
import { getStylesConfig } from '../../../utils/webpack/partials/styles';
|
||||||
|
import { ExecutorContext } from '@nrwl/devkit';
|
||||||
import MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
import MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
import webpackMerge = require('webpack-merge');
|
import webpackMerge = require('webpack-merge');
|
||||||
import postcssImports = require('postcss-import');
|
import postcssImports = require('postcss-import');
|
||||||
@ -25,16 +25,32 @@ interface PostcssOptions {
|
|||||||
config?: string;
|
config?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWebConfig(
|
interface GetWebpackConfigOverrides {
|
||||||
workspaceRoot,
|
root: string;
|
||||||
projectRoot,
|
sourceRoot: string;
|
||||||
sourceRoot,
|
configuration?: string;
|
||||||
options: WebWebpackExecutorOptions,
|
}
|
||||||
|
|
||||||
|
export function getWebpackConfig(
|
||||||
|
context: ExecutorContext,
|
||||||
|
options: NormalizedWebpackExecutorOptions,
|
||||||
esm?: boolean,
|
esm?: boolean,
|
||||||
isScriptOptimizeOn?: boolean,
|
isScriptOptimizeOn?: boolean,
|
||||||
configuration?: string
|
overrides?: GetWebpackConfigOverrides
|
||||||
) {
|
): Configuration {
|
||||||
const tsConfig = readTsConfig(options.tsConfig);
|
const tsConfig = readTsConfig(options.tsConfig);
|
||||||
|
const workspaceRoot = context.root;
|
||||||
|
|
||||||
|
let sourceRoot: string;
|
||||||
|
let projectRoot: string;
|
||||||
|
if (overrides) {
|
||||||
|
projectRoot = overrides.root;
|
||||||
|
sourceRoot = overrides.sourceRoot;
|
||||||
|
} else {
|
||||||
|
const project = context.workspace.projects[context.projectName];
|
||||||
|
projectRoot = project.root;
|
||||||
|
sourceRoot = project.sourceRoot;
|
||||||
|
}
|
||||||
|
|
||||||
if (isScriptOptimizeOn) {
|
if (isScriptOptimizeOn) {
|
||||||
// Angular CLI uses an environment variable (NG_BUILD_DIFFERENTIAL_FULL)
|
// Angular CLI uses an environment variable (NG_BUILD_DIFFERENTIAL_FULL)
|
||||||
@ -56,43 +72,53 @@ export function getWebConfig(
|
|||||||
// TODO(jack): Replace merge behavior with an inlined config so it is easier to understand.
|
// TODO(jack): Replace merge behavior with an inlined config so it is easier to understand.
|
||||||
return webpackMerge.merge([
|
return webpackMerge.merge([
|
||||||
_getBaseWebpackPartial(
|
_getBaseWebpackPartial(
|
||||||
|
context,
|
||||||
options,
|
options,
|
||||||
esm,
|
esm,
|
||||||
isScriptOptimizeOn,
|
isScriptOptimizeOn,
|
||||||
tsConfig.options.emitDecoratorMetadata,
|
tsConfig.options.emitDecoratorMetadata,
|
||||||
configuration
|
overrides
|
||||||
),
|
|
||||||
getPolyfillsPartial(
|
|
||||||
options.polyfills,
|
|
||||||
options.es2015Polyfills,
|
|
||||||
esm,
|
|
||||||
isScriptOptimizeOn
|
|
||||||
),
|
|
||||||
getStylesPartial(
|
|
||||||
wco.root,
|
|
||||||
wco.projectRoot,
|
|
||||||
wco.buildOptions,
|
|
||||||
options.extractCss,
|
|
||||||
options.postcssConfig
|
|
||||||
),
|
),
|
||||||
|
options.target === 'web'
|
||||||
|
? getPolyfillsPartial(
|
||||||
|
options.polyfills,
|
||||||
|
options.es2015Polyfills,
|
||||||
|
esm,
|
||||||
|
isScriptOptimizeOn
|
||||||
|
)
|
||||||
|
: {},
|
||||||
|
options.target === 'web'
|
||||||
|
? getStylesPartial(
|
||||||
|
wco.root,
|
||||||
|
wco.projectRoot,
|
||||||
|
wco.buildOptions,
|
||||||
|
options.extractCss,
|
||||||
|
options.postcssConfig
|
||||||
|
)
|
||||||
|
: {},
|
||||||
getCommonPartial(wco),
|
getCommonPartial(wco),
|
||||||
getBrowserConfig(wco),
|
options.target === 'web' ? getBrowserConfig(wco) : {},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getBaseWebpackPartial(
|
function _getBaseWebpackPartial(
|
||||||
options: WebWebpackExecutorOptions,
|
context: ExecutorContext,
|
||||||
|
options: NormalizedWebpackExecutorOptions,
|
||||||
esm: boolean,
|
esm: boolean,
|
||||||
isScriptOptimizeOn: boolean,
|
isScriptOptimizeOn: boolean,
|
||||||
emitDecoratorMetadata: boolean,
|
emitDecoratorMetadata: boolean,
|
||||||
configuration?: string
|
overrides?: GetWebpackConfigOverrides
|
||||||
) {
|
) {
|
||||||
let partial = getBaseWebpackPartial(options, {
|
let partial = getBaseWebpackPartial(
|
||||||
esm,
|
options,
|
||||||
isScriptOptimizeOn,
|
{
|
||||||
emitDecoratorMetadata,
|
esm,
|
||||||
configuration,
|
isScriptOptimizeOn,
|
||||||
});
|
emitDecoratorMetadata,
|
||||||
|
configuration: overrides?.configuration ?? context.configurationName,
|
||||||
|
},
|
||||||
|
context
|
||||||
|
);
|
||||||
delete partial.resolve.mainFields;
|
delete partial.resolve.mainFields;
|
||||||
return partial;
|
return partial;
|
||||||
}
|
}
|
||||||
@ -256,7 +282,7 @@ export function getPolyfillsPartial(
|
|||||||
// Safari 10.1 supports <script type="module"> but not <script nomodule>.
|
// Safari 10.1 supports <script type="module"> but not <script nomodule>.
|
||||||
// Need to patch it up so the browser doesn't load both sets.
|
// Need to patch it up so the browser doesn't load both sets.
|
||||||
config.entry.polyfills = [
|
config.entry.polyfills = [
|
||||||
require.resolve('@nrwl/web/src/utils/webpack/safari-nomodule.js'),
|
require.resolve('@nrwl/webpack/src/utils/webpack/safari-nomodule.js'),
|
||||||
...(polyfills ? [polyfills] : []),
|
...(polyfills ? [polyfills] : []),
|
||||||
];
|
];
|
||||||
} else if (es2015Polyfills && !esm && isScriptOptimizeOn) {
|
} else if (es2015Polyfills && !esm && isScriptOptimizeOn) {
|
||||||
@ -304,3 +330,15 @@ export function getCSSModuleLocalIdent(
|
|||||||
// Remove the .module that appears in every classname when based on the file and replace all "." with "_".
|
// Remove the .module that appears in every classname when based on the file and replace all "." with "_".
|
||||||
return className.replace('.module_', '_').replace(/\./g, '_');
|
return className.replace('.module_', '_').replace(/\./g, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function convertBuildOptions(
|
||||||
|
buildOptions: NormalizedWebpackExecutorOptions
|
||||||
|
): any {
|
||||||
|
const options = buildOptions as any;
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
buildOptimizer: options.optimization,
|
||||||
|
forkTypeChecker: false,
|
||||||
|
lazyModules: [] as string[],
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,57 +1,73 @@
|
|||||||
import { resolve, dirname, relative, basename } from 'path';
|
import { basename, dirname, relative, resolve } from 'path';
|
||||||
import {
|
|
||||||
AdditionalEntryPoint,
|
|
||||||
BuildNodeBuilderOptions,
|
|
||||||
NormalizedBuildNodeBuilderOptions,
|
|
||||||
} from './types';
|
|
||||||
import { statSync } from 'fs';
|
import { statSync } from 'fs';
|
||||||
|
import { normalizePath } from '@nrwl/devkit';
|
||||||
|
|
||||||
export interface FileReplacement {
|
import type {
|
||||||
replace: string;
|
AssetGlobPattern,
|
||||||
with: string;
|
FileReplacement,
|
||||||
}
|
WebpackExecutorOptions,
|
||||||
|
NormalizedWebpackExecutorOptions,
|
||||||
|
} from '../schema';
|
||||||
|
|
||||||
export function normalizeBuildOptions(
|
export function normalizeOptions(
|
||||||
options: BuildNodeBuilderOptions,
|
options: WebpackExecutorOptions,
|
||||||
root: string,
|
root: string,
|
||||||
sourceRoot: string,
|
sourceRoot: string
|
||||||
projectRoot: string
|
): NormalizedWebpackExecutorOptions {
|
||||||
): NormalizedBuildNodeBuilderOptions {
|
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
projectRoot,
|
target: options.target ?? 'web',
|
||||||
main: resolve(root, options.main),
|
main: resolve(root, options.main),
|
||||||
outputPath: resolve(root, options.outputPath),
|
outputPath: resolve(root, options.outputPath),
|
||||||
tsConfig: resolve(root, options.tsConfig),
|
tsConfig: resolve(root, options.tsConfig),
|
||||||
fileReplacements: normalizeFileReplacements(root, options.fileReplacements),
|
fileReplacements: normalizeFileReplacements(root, options.fileReplacements),
|
||||||
assets: normalizeAssets(options.assets, root, sourceRoot),
|
assets: normalizeAssets(options.assets, root, sourceRoot),
|
||||||
webpackConfig: options.webpackConfig
|
webpackConfig: normalizePluginPath(options.webpackConfig, root),
|
||||||
? []
|
optimization:
|
||||||
.concat(options.webpackConfig)
|
typeof options.optimization !== 'object'
|
||||||
.map((path) => normalizePluginPath(path, root))
|
? {
|
||||||
: [],
|
scripts: options.optimization,
|
||||||
additionalEntryPoints: normalizeAdditionalEntries(
|
styles: options.optimization,
|
||||||
root,
|
}
|
||||||
options.additionalEntryPoints ?? []
|
: options.optimization,
|
||||||
),
|
polyfills: options.polyfills ? resolve(root, options.polyfills) : undefined,
|
||||||
outputFileName: options.outputFileName ?? 'main.js',
|
es2015Polyfills: options.es2015Polyfills
|
||||||
deleteOutputPath: options.deleteOutputPath ?? true,
|
? resolve(root, options.es2015Polyfills)
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
function normalizeFileReplacements(
|
||||||
|
root: string,
|
||||||
|
fileReplacements: FileReplacement[]
|
||||||
|
): FileReplacement[] {
|
||||||
|
return fileReplacements.map((fileReplacement) => ({
|
||||||
|
replace: resolve(root, fileReplacement.replace),
|
||||||
|
with: resolve(root, fileReplacement.with),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeAssets(
|
export function normalizePluginPath(pluginPath: void | string, root: string) {
|
||||||
|
if (!pluginPath) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require.resolve(pluginPath);
|
||||||
|
} catch {
|
||||||
|
return resolve(root, pluginPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeAssets(
|
||||||
assets: any[],
|
assets: any[],
|
||||||
root: string,
|
root: string,
|
||||||
sourceRoot: string
|
sourceRoot: string
|
||||||
): any[] {
|
): AssetGlobPattern[] {
|
||||||
if (!Array.isArray(assets)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return assets.map((asset) => {
|
return assets.map((asset) => {
|
||||||
if (typeof asset === 'string') {
|
if (typeof asset === 'string') {
|
||||||
const resolvedAssetPath = resolve(root, asset);
|
const assetPath = normalizePath(asset);
|
||||||
|
const resolvedAssetPath = resolve(root, assetPath);
|
||||||
const resolvedSourceRoot = resolve(root, sourceRoot);
|
const resolvedSourceRoot = resolve(root, sourceRoot);
|
||||||
|
|
||||||
if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) {
|
if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) {
|
||||||
@ -78,7 +94,8 @@ function normalizeAssets(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedAssetPath = resolve(root, asset.input);
|
const assetPath = normalizePath(asset.input);
|
||||||
|
const resolvedAssetPath = resolve(root, assetPath);
|
||||||
return {
|
return {
|
||||||
...asset,
|
...asset,
|
||||||
input: resolvedAssetPath,
|
input: resolvedAssetPath,
|
||||||
@ -88,34 +105,3 @@ function normalizeAssets(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeFileReplacements(
|
|
||||||
root: string,
|
|
||||||
fileReplacements: FileReplacement[]
|
|
||||||
): FileReplacement[] {
|
|
||||||
return fileReplacements.map((fileReplacement) => ({
|
|
||||||
replace: resolve(root, fileReplacement.replace),
|
|
||||||
with: resolve(root, fileReplacement.with),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizePluginPath(path: string, root: string) {
|
|
||||||
try {
|
|
||||||
return require.resolve(path);
|
|
||||||
} catch {
|
|
||||||
return resolve(root, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeAdditionalEntries(
|
|
||||||
root: string,
|
|
||||||
additionalEntries: AdditionalEntryPoint[]
|
|
||||||
) {
|
|
||||||
return additionalEntries.map(
|
|
||||||
({ entryName, entryPath }) =>
|
|
||||||
({
|
|
||||||
entryName,
|
|
||||||
entryPath: resolve(root, entryPath),
|
|
||||||
} as AdditionalEntryPoint)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user